当前还处于 beta
版本, 想要正式使用在项目里还须要一段的时间, 可是结构与api变化应该不大了.前端
这里列出的并不全, 可是够用了 1. alpha 内测版本 2. beta 公测版本 3. Gamma 正式发布的候选版本 4. Final 正式版 5. plus 加强版 6. full 彻底版 7. Trial 试用版(通常有时间或者功能限制)
1. 学一门新鲜的技术,就像练习王者荣耀新出的英雄同样, 探索他的好玩之处能够给开发者带来快乐, 使用新的好的技术会让工做更愉悦 2. 这个版本的vue 相似"个人世界", 所有都是一个个方块组成, 不要小看方块, 方块多了甚至能够组成圆形(量变引发质变), 新鲜的玩法才能激发咱们的兴趣
1. npm install -g @vue/cli cli升级到4版本 2. 随便建立个项目, vue create next-test 3. 选择配置最好把router与vuex一块儿选上, 方便后续升级 4. vue add vue-next cli提供的进化到下一版本的命令, 执行后自动将router, vuex 升级到alpha版.
1. 模板编译速度的提高, 对静态数据的跳过处理. 2. 对数组的监控 3. 对ts有了很好的支持 4. 对2.x版本的彻底兼容 5. 能够有多个根节点 (也有bug, 好比外层开了display:flex 那么里面会收到影响, 也就是说布局更灵活但也更要当心, 总之请对本身与他人的代码负责) 6. 支持Source map, 虽然没演示可是这点真的重要
import { createApp } from 'vue'; import App from './App.vue' import router from './router' import store from './store' // 方法一. 建立实例变成了链式, 直接写下去感受语义与结构有点模糊, 可是咱们要理解vue这样作的良苦用心, 前端趋近于函数化. // createApp(App).use(router).use(store).mount('#app') // 方法二. const app = createApp(App); app.use(router); app.use(store); app.mount('#app');
import { createStore } from 'vuex' // 专门建立实例的一个方法 export default createStore({ state: { }, mutations: { }, actions: { }, modules: { } });
import { createRouter, createWebHistory } from 'vue-router'; import Home from '../views/Home.vue' const routes = [ { path: '/', name: 'Home', component: Home } ] const router = createRouter({ // 专门建立history的函数 history: createWebHistory(process.env.BASE_URL), routes }) export default router
1: refvue
import { ref } from "vue"; export default { // 1: 这个版本基本逻辑都在setup里面完成了, 有人说把他当成2.x的data. setup() { // 2: 定义一个追踪的变量,也就是双向绑定. const n = ref(1); // 生成的n是一个对象, 这样方便vue去监控它 function addN() { n.value++; // 注意这里要使用.value的形式, 由于n是对象↑, value才是他的值 } return { n, // 返回出去页面的template才可使用它, {{n}} 不须要.value addN } } }
2: reactivereact
import { reactive, toRefs } from "vue"; export default { setup() { // 注意事项: reactive的对象不能够结构返回或导入, 会致使失去响应式 const obj = reactive({ name: "金毛", age: 4 }); function addObj() { obj.age++; } return { ...obj, // 这样写很差, 里面会失去响应式 obj, // 这样写那么外面就要都基于obj来调取, 类型{{obj.age}} ...toRefs(obj) // 必须是reactive生成的对象, 普通对象不能够, 他把每一项都拿出来包了一下, 咱们能够这样用了 {{age}}, 放心我们多深的obj也能够响应 } } }
<div> <div ref="content">第一步, 在dom上面定义, 他会有一个回调</div> </div> <ul> <li>v-for 出来的ref</li> <li>能够写为表达式的形式, 能够推导出vue是如何实现的</li> <li>vue2.x的时候v-for不用这么麻烦, 直接写上去会被组装成数组</li> <li :ref="el => { items[index] = el }" v-for="(item,index) in 6" :key="item">{{item}}</li> </ul>
import { ref, onMounted, onBeforeUpdate } from "vue";
export default { setup() { // 2: 定义一个变量接收dom, 名字无所谓, 可是与dom统一的话会有很好的语义化 const content = ref(null); const items = ref([]); // 4: 在生命周期下, 这个值已经完成了变化, 因此固然第一时间就拿到 onMounted(() => { console.log(content.value); console.log("li标签组", items.value); }); // 5: 确保在每次变动以前重置引用 onBeforeUpdate(() => { items.value = []; }); // 3: 返出去的名称要与dom的ref相同, 这样就能够接收到dom的回调 return { content, items }; } };
<template> <div> <button @click="add">点击增长, 触发updata</button> <p>{{obj.count}}</p> </div> <p> 2.x与 3.0的对照 beforeCreate -> 使用 setup() created -> 使用 setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted errorCaptured -> onErrorCaptured </p> </template> <script> //这些生命周期注册方法只能用在 setup 钩子中 import { onMounted, onUpdated, onBeforeUnmount, reactive } from "vue"; export default { // 1: setup显得冗长, 能够本身动手作一些插件来优化 // 2: 自己更加明确意图 // 3: 须要树立工程师的正确代码意识 // 4: 能力不足可能会写出更差的代码 // 做者说: 提高上界的收益远远大于下降下界的损失。值得深思, 前端须要提升门槛 // 5: 调用时机: 建立组件实例,而后初始化 props ,紧接着就调用setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子以前被调用 // 6: 这些生命周期钩子注册函数只能在 setup() 期间同步使用, 由于它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup() 的组件实例), 不在当前组件下调用这些函数会抛出一个错误。 // 7: 原则上生命周期里面不会放具体的逻辑,哪怕只有一句赋值一个三元都不可放, 这也正好符合当前的工程模式 // 讨论: 有多少种方式, 能够判断出某个函数 当前处于哪一个函数? // 好比有多层嵌套的组件是否有影响 setup() { onMounted(() => { console.log("is mounted!"); }); onUpdated(() => { console.log("is onUpdated!"); }); onBeforeUnmount(() => { console.log("is onBeforeUnmount!"); }); const obj = reactive({ count: 1 }); function add() { obj.count++; } return { obj, add }; } }; </script>
<template> <div> {{id}} </div> </template> <script> import { getCurrentInstance, ref } from 'vue'; export default { setup(){ const { ctx } = getCurrentInstance() // 1. 这样也是为了去掉this // 2. 方便类型推导 console.log(ctx.$router); // push等方法 console.log(ctx.$router.currentRoute.value); // 路由实例 // 这个其实没有必要变成ref由于这个值不必动态 // 可是他太长了, 这个真的不能忍 const id = ref(ctx.$router.currentRoute.value.query.id) // 4: 页面拦截器 ctx.$router.beforeEach((to, from,next)=>{ console.log('路由的生命周期') next() }) return { id } } } </script>
import { createStore } from 'vuex' // 难道前端趋势只有函数这一种吗 export default createStore({ state: { name:'牛逼, 你拿到我了', age: 24, a:'白', b:'黑' }, mutations: { updateName(state, n){ state.name += n } }, actions: { deferName(store) { setTimeout(()=>{ // 必须只有commit能够修改值, 这个设定我比较反对, 能够讨论 // vuex自己结构就很拖沓, 定义域使用我的都不喜欢 store.state.name = '牛逼, 你改回来了' },1000) } }, getters: { fullName(state){ return `${state.a} - + -${state.b}` } }, modules: { } });
<template> <div> <p>{{name}}</p> <button @click="updateName('+')">点击改变名字</button> <br /> <button @click="deferName('+')">改回来</button> <p>{{fullName}}</p> </div> </template> <script> import { useStore } from "vuex"; import { computed } from "vue"; export default { setup() { const store = useStore(); // 1: 单个引入 const name = computed(() => store.state.name); // 2: 引入整个state const state = computed(() => store.state); console.log("vuex的实例", state.value); // 别忘了.value // 3: 方法其实就直接从本体上取下来了 const updateName = newName => store.commit("updateName", newName); // 4: action一个意思 const deferName = () => store.dispatch("deferName"); // 5: getter 没变化 const fullName = computed(() => store.getters.fullName); return { name, fullName, deferName, updateName, }; } }; </script>
<template> <div> <button @click="addN1">上面的增长</button>---> {{n1}} </div> <div> <button @click="addN2">下面的增长</button>---> {{n2}} <button @click="addN210">每次n2+10</button> </div> <div> <p>组件里面也能够如此引用, 这就能够代替mixin一部分功能了</p> <button @click="addN3">n3的增长</button>---> {{n3.value}} </div> <div> <com></com> </div> </template> <script> import { ref} from 'vue'; import n3Change from './mixin'; import com from '../components/composition.vue'; export default { components:{ com }, setup(){ // 1: setup只是一个整合函数 // 2: 甚至整个函数里面可能会没有具体的逻辑 // 3: 以此推断, ref等方式定义的变量, 会自动识别在哪一个setup内部, 从而达到逻辑的复用 // 4: 由此方法能够很好的代替mixin了 // 5: 固然, 这里也能够截获数据,来作一些事情 const {n2, addN2} = n2Change(); function addN210(){ n2.value += 10 } return { ...n1Change(), ...n3Change(), n2, addN2, addN210 } } } // 甚至已经能够写在任何地方了, 响应式自由度大大提升 function n1Change(){ const n1 = ref(1); let addN1 = ()=>{ n1.value++ } return { n1, addN1 } } function n2Change(){ const n2 = ref(1); let addN2 = ()=>{ n2.value++ } return { n2, addN2 } } </script>
写在任何地方, 而后导入就成了mixinios
import { reactive } from 'vue'; export default ()=> { const n3 = reactive({ name: 'mixin', value: 1 }) const addN3=()=>{ n3.value++ } return { n3, addN3 } }
// 开发插件并不必定要挂载到vue的原型上 // 致使vue原型臃肿, 命名冲突等等(好比两个ui都叫 message) // 原理就是 provide 和 inject, 依赖注入. import {provide, inject} from 'vue'; // 这里使用symbol就不会形成变量名的冲突了, 这个命名权交给用户才是真正合理的架构设计 const StoreSymbol = Symbol() export function provideString(store){ provide(StoreSymbol, store) } export function useString() { const store = inject(StoreSymbol) return store }
app.vue页面统一的初始化一下vue-router
export default { setup(){ // 一些初始化'配置/操做'能够在这里进行 // 须要放在对应的根节点, 由于依赖provide 和 inject provideString({ a:'可能我是axios', b:'可能我是一个message弹框' }) } }
在须要使用的组件里面接收vuex
<template> <div> 插件的演示 </div> </template> <script> import { useString } from '../插件'; export default { setup(){ const store = useString(); // 不光是拿到值, 能够由app定义什么能够被拿到 console.log('拿到值了',store) } } </script>
<template> <div> <button @click="addn1">n1增长--{{n1}}</button> <button @click="addn2">n2增长--{{n2}}</button> <button @click="addn3">n3增长--{{n3}}</button> </div> </template> <script> import { watch, ref } from "vue"; export default { setup() { const n1 = ref(1); const n2 = ref(1); const n3 = ref(1); // 1: 监听一个 // 第一个参数是函数返回值, 固然也能够 直接写n1 // 若是监听的是一个对象里面的某个属性, 那就须要这种函数的写法了, 比2.x的字符串写法高明不少 watch( () => n1.value, (val, oldVal) => { console.log("新值", val); console.log("老值", oldVal); } ); // 2: 监听多个 // 数组的形式定义多个, 这就出现问题了吧, 若是我观察的对象就是个数组, 而且每一项都是一个返回值的函数, 岂不是会被他误认为是多监控的结构, 苦恼 watch( [() => n2.value, ()=>n3.value], ([val, val3],[val2, val4]) => { // val 是 n2的新值 val2是 n2的老值 // val3 是 n3的新值 val4是 n3的老值 console.log("新值 与 老值 是这种对应关系", val, val2); console.log("新值 与 老值 是这种对应关系", val3, val4); } ); function addn1() { n1.value++; } function addn2() { n2.value++; } function addn3() { n3.value++; } return { addn1, addn2, addn3, n1, n2, n3 }; } }; </script>
<template> <div> <button @click="addCount">点击计算</button> <button @click="setCount(1)">点击出发set</button> <p>count--{{count}}</p> <p>count2--{{count2}}</p> <p>count3--{{count3}}</p> </div> </template> <script> // 弄得相似react了 import { computed, ref, watchEffect } from "vue"; export default { setup() { const count = ref(1); // 1. 默认的定义方式 const count2 = computed(() => count.value * 2); console.log(count2.value); // 也要value由于多是简单类型 // 2. getter与setter固然能够定义 const count3 = computed({ get: () => count.value * 3, set: val => { // 这里重置了count count.value = val; } }); // 3. watchEffect 更像是计算函数 // 当即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变动时从新运行该函数 // 侦听器会被连接到该组件的生命周期,并在组件卸载时自动中止。 // Vue 的响应式系统会缓存反作用函数,并异步地刷新它, 好比同时改变了count与conut4此时watchEffect只是执行一次 // 初始化运行是在组件 mounted 以前执行的。所以,若是你但愿在编写反作用函数时访问 DOM(或模板 ref),请在 onMounted 钩子中进行 // 并非返回值, 而是监听里面全部的值, 任何有变化都会从新执行, 他应该能够玩出点东西。 const count4 = ref(1); const stop = watchEffect(() => { if (count4.value) { console.log("做为判断条件也能够根据count4的变化而从新执行"); } console.log(count.value); }); setTimeout(() => { stop(); // 中止监听 }, 10000); function addCount() { count.value++; setTimeout(() => { count4.value++; }, 1000); } // 触发setter function setCount() { count3.value = 2; } return { count, count2, addCount, count3, setCount }; } }; </script>
<template> <div> <input type="text" v-model="text" /> </div> </template> <script> import { customRef, onUnmounted } from "vue"; export default { setup() { let timeout = null; // 并不须要响应式 const text = useDebouncedRef("hello", (time) => { // 毕竟是延时的不用担忧获取不到这个值 console.log("延时执行回调", text.value); console.log('时间实例', time) timeout = time; }); // 好习惯也是成为合格工程师的必要条件 onUnmounted(()=>{ clearTimeout(timeout); }) return { text }; } }; // 并不用纯粹的js编写, 能够利用customRef来监控这个值的一举一动 // 写法通常, 可是思路又多了一条, 感谢 function useDebouncedRef(value, callback, delay = 200) { let timeout; // 两个参数分别是用于追踪的 track 与用于触发响应的 trigger // 这两个参数对 值的追踪 在当前并无用,好比watchEffect的出发机制 // 不调用这两个值没问题, 可是若是写成插件的话仍是要调用的, 由于别人没准在追踪这个值, // 注意: 这个函数不能够有太大的delay, 若是超过500的话就须要考虑在组件销毁时候的清除定时器, 反而逻辑加深了, 此时咱们能够每次把演示器的实例拿到 return customRef((track,trigger) => { return { get() { track() return value; }, set(newValue) { clearTimeout(timeout); // callback接受的太晚了, 能够在这里用另外一个函数或对象接收 timeout = setTimeout(() => { value = newValue; trigger() callback(timeout); }, delay); } }; }); } </script>
父级npm
<template> <div> 组件: <zj :type="type" @ok="wancheng"></zj> </div> </template> <script> import zj from "../components/子组件.vue"; import { ref } from 'vue'; import { provide } from 'vue' export default { components: { zj }, setup() { provide('name','向下传值'); // 基础值 provide('name2', ref('向下传值')); // 监控值 const type = ref('大多数'); function wancheng(msg){ console.log('子组件-->',msg) setTimeout(()=>{ type.value = 'xxxxxxx' },2000) } return { type, wancheng } } }; </script>
子组件编程
<template> <div>props的属性不用setup去return --- {{type}}</div> </template> <script> import { inject, ref } from 'vue' // 为了让 TypeScript 正确的推导类型,咱们必须使用 createComponent 来定义组件: export default { props: { type: String }, // 1: props也是不能够解构的, 会失去响应式 // 2: context是上下文, 咱们能够获取到slots emit 等方法 // 3: props, context 分开也是为了ts更明确的类型推导 // setup({type}){ setup(props, context) { // 1: props console.log("props", props.type); console.log("上下文", context); context.emit('ok','传递完成') // 2: 注入 console.log('inject',inject('name')); console.log('inject',inject('xxxx','我是默认值')) inject('name1', ref('默认值')) // 接收方也能够这样 } }; </script>
每次看到新技术都会感受挺好玩的, 一成不变的生活会太无趣了, 在某些方面讲vue失去了一些原本的优点, 可是人家能够兼容vue2.x那就没的说了, 做为分享会的稿子的话时间差很少一个小时, 最好每一个点都现场手敲, 光让你们看已经写好的代码会走神的, 我在学习视频的时候最不喜欢的就是老师说"这个我就不演示了".
此次就这么多, 但愿和你一块儿进步.axios