前言 {#前言}
最近看了看vue3,发现变化还是挺大的,写篇文章来记录一波
vite {#vite}
vite介绍和用法 {#vite介绍和用法}
Vite是一个由原生ESM
驱动的Web开发构建工具。在开发环境下基于浏览器原生ES imports开发,在生产环境下基于Rollup打包。
其实简单来说,vite就是一个和webpack用处差不多的代码构建工具,但是它在代码开发阶段有着非常显著的优势,它大大降低了开启本地服务器和代码热更新需要的时间,他的主要优点有下面几个
- 快速的冷启动
- 即时的模块热更新
- 真正的按需编译
那么怎么使用呢,我们直接运行下面的命令就可以了
|-----------------|--------------------------------------------------------------------------------------------|
| 1 2 3 4
| $ npm init vite-app <project-name> $ cd <project-name> $ npm install $ npm run dev
|
这个命令会在本地临时安装vite,然后用vite创建一个新项目,所以每次运行的时候使用的都是最新的vite
而从生成的目录树中可以看出来,vite和vue-cli生成的代码并没有太大的差别
|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| ├── index.html ├── package.json ├── public │ └── favicon.ico └── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── index.css └── main.js
|
在开发时要注意在导入文件时除了导入的文件是js类型的,其他时候都要补全后缀
vite的原理 {#vite的原理}
在运行npm run dev
后,vite借用了koa启动了一个本地代理服务器,没有进行任何的编译和打包操作
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| [vite] Optimizable dependencies detected: vue Dev server running at: > Network: http://192.168.2.67:3000/ > Local: http://localhost:3000/
|
在访问http://localhost:3000/
时,vite直接返回了项目的index.html文件
|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vite App</title> </head> <body> <div id="app"></div> <script type="module" src="/src/main.js"></script> </body> </html>
|
然后浏览器就会解析这段html文件,值的一提的是,这里的main.js是使用了module的方式引入的,所以天生支持import和export语句,浏览器会向本地服务器请求main.js
本地服务器返回处理过的main.js
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| import {createApp} from '/@modules/vue.js' import App from '/src/App.vue' import '/src/index.css?import' createApp(App).mount('#app');
|
浏览器拿到main.js后,继续解析并请求依赖的文件,服务器对请求的文件进行编译和处理,返回给浏览器运行,一直重复这个过程
编译过后的index.css
再来看看vite的几个优点
- 快速的冷启动(开启服务器不进行打包和编译,只是开启一个服务器返回index.html)
- 即时的模块热更新
- 真正的按需编译(浏览器的import语法天生支持按需引入,服务器只对浏览器请求的文件进行编译)
Api和数据响应式的变化 {#Api和数据响应式的变化}
去掉了Vue构造函数 {#去掉了Vue构造函数}
从刚刚vite搭建的项目中可以看到,vue3不再使用new Vue()
的方式来创建vue应用,而是使用createApp()
来创建,为什么要这么做呢,来看下面的例子
|------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!-- vue2 --> <div id="app1"></div> <div id="app2"></div> <script> Vue.use(...); // 此代码会影响所有的vue应用 Vue.mixin(...); // 此代码会影响所有的vue应用 Vue.component(...); // 此代码会影响所有的vue应用 new Vue({ // 配置 }).$mount("#app1") new Vue({ // 配置 }).$mount("#app2") </script>
|
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| <!-- vue3 --> <div id="app1"></div> <div id="app2"></div> <script> createApp(根组件).use(...).mixin(...).component(...).mount("#app1") createApp(根组件).mount("#app2") </script>
|
可以看出,vue2的一些方法会影响所有的vue应用,而vue3就可以解决了这个问题,让开发者可以对不同的Vue应用进行不同的配置,方便了不同Vue应用的相互隔离
另外createApp创建的是一个Vue应用,Vue应用不是一个特殊的组件,这点是和Vue2不同的,Vue3对这两个概念进行了区分,一定程度上避免了可能造成的思维混乱
最后一点就是Vue2的构造函数集成了太多功能,不利于tree shaking,Vue3把这些功能使用普通函数导出,能够充分利用tree shaking优化打包体积,所以Vue3的打包体积是小于Vue2的
使用了proxy来进行数据响应式 {#使用了proxy来进行数据响应式}
优点:
- 使用proxy比递归对象设置访问器属性要高效
- 可以观测到对象的新增属性和删除属性
- 可以观测到数组下标的变化
缺点:
- 兼容性较差。而且polyfill也难以完全支持
模板的变化 {#模板的变化}
组件允许多个根节点 {#组件允许多个根节点}
如图,下图的模板语法现在被支持了,在一个组件里允许了多个根结点而存在
|-------------------------|--------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| <template> <div> wow </div> <div> funny </div> </template>
|
实现原理非常easy
|-------------|---------------------------------|
| 1 2
| <div></div> <div></div>
|
编译后
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10
| import { createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue" export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock(_Fragment, null, [ _createElementVNode("div"), _createElementVNode("div") ], 64 /* STABLE_FRAGMENT */)) } // Check the console for the AST
|
v-model的升级 {#v-model的升级}
vue2
提供了两种双向绑定:v-model
和.sync
,在vue3
中,去掉了.sync
修饰符,只需要使用v-model
进行双向绑定即可。
为了让v-model
更好的针对多个属性进行双向绑定,vue3
作出了以下修改
- 当对自定义组件使用
v-model
指令时,绑定的属性名由原来的value
变为modelValue
,事件名由原来的input
变为update:modelValue
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12
| <!-- vue2 --> <ChildComponent :value="pageTitle" @input="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model="pageTitle" /> <!-- vue3 --> <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model="pageTitle" />
|
- 去掉了
.sync
修饰符,它原本的功能由v-model
的参数替代
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9
| <!-- vue2 --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent :title.sync="pageTitle" /> <!-- vue3 --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> <!-- 简写为 --> <ChildComponent v-model:title="pageTitle" />
|
详细写法
|------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <template> <div class="ChildView"> <input type="text" :value="inputValue" @input="updateInputValue"> </div> </template> <script lang="ts"> import {defineComponent} from 'vue'; export default defineComponent({ name: 'Child', props : { inputValue : { type : String } }, methods : { updateInputValue($event : any) { this.$emit('update:inputValue', $event.target.value) } } }); </script>
|
|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div class="wrapper"> <div> <Child v-model:inputValue="childValue"></Child> </div> </div> </template> <script lang="ts"> import {defineComponent} from 'vue'; import Child from "@/components/Child.vue"; export default defineComponent({ name: 'HelloWorld', components: { Child, }, data() { return { childValue : "" } } }); </script>
|
相关文档:vue3 : v-model
修改v-if和v-for的优先级 {#修改v-if和v-for的优先级}
vue2的优先级是v-for高于v-if
|---------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div class="about"> <h1>This is an about page</h1> <ul> <li v-for="(item, index) in items" v-if="item.flag">{{item.value}}</li> </ul> </div> </template> <script lang="ts"> import {Component, Vue} from 'vue-property-decorator'; @Component({}) export default class About extends Vue { public items : Array<{ flag : boolean, value : string }> = [{ flag : false, value : "rua" }, { flag : true, value : "qaq" }] } </script>
|
渲染结果
vue3的优先级是v-if高于v-for
|------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div> <p v-for="(item, index) in items" v-if="item.flag">{{item.value}}</p> </div> </template> <script> export default { data() { return { items : [ { flag : false, value : "rua" }, { flag : true, value : "qaq" } ] } } } </script>
|
这段代码会报错
Vue效率提升 {#Vue效率提升}
静态节点提升 {#静态节点提升}
编译时期会对静态节点和静态属性进行提升,用于这减少render函数中创建VNode的消耗
|-------------------|-----------------------------------------------------------------------------|
| 1 2 3 4 5
| <template> <div id="app"> <div>Hello world</div> </div> </template>
|
编译结果
|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13
| import {createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock} from "/@modules/vue.js" const _hoisted_1 = { id: "app" } const _hoisted_2 = /*#__PURE__*/ _createVNode("div", null, "Hello world", -1 /* HOISTED */ ) export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", _hoisted_1, [_hoisted_2])) }
|
预字符串化 {#预字符串化}
当编译器遇到大量连续的静态内容,将这些静态节点序列化为字符串并生成一个Static类型的VNode,静态节点在运行时会通过 innerHTML来创建真实节点
|---------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div class="container"> <div class="logo"> <h1>logo</h1> </div> <ul class="nav"> <li><a href="">menu1</a></li> <li><a href="">menu2</a></li> <li><a href="">menu3</a></li> <li><a href="">menu4</a></li> <li><a href="">menu5</a></li> </ul> <div class="user"> <span>{{ user.name }}</span> </div> </div> </template>
|
|------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import {createVNode as _createVNode, toDisplayString as _toDisplayString, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createBlock as _createBlock} from "/@modules/vue.js" const _hoisted_1 = { class: "container" } const _hoisted_2 = /*#__PURE__*/ _createStaticVNode("<div class=\"logo\"><h1>logo</h1></div><ul class=\"nav\"><li><a href=\"\">menu1</a></li><li><a href=\"\">menu2</a></li><li><a href=\"\">menu3</a></li><li><a href=\"\">menu4</a></li><li><a href=\"\">menu5</a></li></ul>", 2) const _hoisted_4 = { class: "user" } export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", _hoisted_1, [_hoisted_2, _createVNode("div", _hoisted_4, [_createVNode("span", null, _toDisplayString(_ctx.user.name), 1 /* TEXT */ )])])) }
|
预字符串化在下面两种情况出现:
- 如果节点没有属性,有连续20个及以上的静态节点存在
- 连续的节点中有5个及以上的节点是有属性绑定的节点
事件处理函数缓存 {#事件处理函数缓存}
对下面的模板
|-------------------|------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| <template> <div class="container"> <button @click="handleClick"></button> </div> </template>
|
编译结果
|------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12
| import {createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock} from "/@modules/vue.js" const _hoisted_1 = { class: "container" } export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", _hoisted_1, [_createVNode("button", { onClick: _cache[1] || (_cache[1] = (...args)=>($setup.handleClick(...args))) })])) }
|
相比于vue2的编译结果
|-----------------------|----------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| render(ctx){ return createVNode("button", { onClick: function(){ // ... } }) }
|
Block Tree {#Block-Tree}
在线尝试:https://vue-next-template-explorer.netlify.app/
vue2在对比新旧树的时候,并不知道哪些节点是静态的,哪些是动态的,因此只能一层一层比较,这就浪费了大部分时间在比对静态节点上。
vue3新增了block tree这一个概念,block tree会对动态变化的节点进行标记,从而减少了vue2一层层diff的时间,在更新时也只要查找动态的节点就可以了(动态的节点会存到一个数组里用于查找),也就是说,把一个树的diff拍平成了数组的diff
你可以从vNode的dynamicChildren
属性里看到动态节点,动态节点的标记是在createVNode
时标记的
结构不稳定(比如v-if)和结构数量(v-for)不一样的,会重新创建block
节点
比如
|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <div> <template v-if="flag"> <div>{{name}}</div> <div>Sakura</div> </template> <template v-else> <div>{{age}}</div> <div>Snow</div> </template> </div> <div> <span v-for="item in arr">{{item}}</span> </div>
|
编译后
|------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode, renderList as _renderList } from "vue" export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock(_Fragment, null, [ _createElementVNode("div", null, [ (_ctx.flag) ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [ _createElementVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */), _createElementVNode("div", null, "Sakura") ], 64 /* STABLE_FRAGMENT */)) : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [ _createElementVNode("div", null, _toDisplayString(_ctx.age), 1 /* TEXT */), _createElementVNode("div", null, "Snow") ], 64 /* STABLE_FRAGMENT */)) ]), _createElementVNode("div", null, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.arr, (item) => { return (_openBlock(), _createElementBlock("span", null, _toDisplayString(item), 1 /* TEXT */)) }), 256 /* UNKEYED_FRAGMENT */)) ]) ], 64 /* STABLE_FRAGMENT */)) } // Check the console for the AST
|
PatchFlag {#PatchFlag}
vue2在对比每一个节点时,并不知道这个节点哪些相关信息会发生变化,因此要将节点的所有属性进行一次比对
PatchFlag可以标记一个节点具体哪些内容更新了,比如说
数字 1:代表节点有动态的 textContent
数字 2:代表元素有动态的 class 绑定
数字 3:代表xxxxx
比如下面这段代码
|-------------------|---------------------------------------------------------------------------------|
| 1 2 3 4 5
| <template> <div class="container" :id="id"> {{name}} </div> </template>
|
编译结果
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", { class: "container", id: $setup.id }, _toDisplayString($setup.name), 9 /* TEXT, PROPS */ , ["id"])) }
|
9这个数字就标记了div标签里可能变化的内容
Composition Api {#Composition-Api}
setup {#setup}
setup函数会在所有生命周期函数前执行,这个函数是使用Composition API的入口
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| export default { setup(props, context){ // 该函数在组件属性被赋值后立即执行,早于所有生命周期钩子函数 // props 是一个对象,包含了所有的组件属性值 // context 是一个对象,提供了组件所需的上下文信息 } }
|
context对象的成员
| 成员 | 类型 | 说明 |
|-------|----|-----------------------|
| attrs | 对象 | 同vue2
的this.$attrs
|
| slots | 对象 | 同vue2
的this.$slots
|
| emit | 方法 | 同vue2
的this.$emit
|
数据响应式的创建 {#数据响应式的创建}
| API | 传入 | 返回 | 备注 |
|:-----------|---------------------------|------------------|----------------------------------------------------------------------|
| reactive
| plain-object
| 对象代理
| 深度代理对象中的所有成员 |
| readonly
| plain-object
or proxy
| 对象代理
| 只能读取代理对象中的成员,不可修改 |
| ref
| any
| { value: ... }
| 对value的访问是响应式的 如果给value的值是一个对象, 则会通过reactive
函数进行代理 如果已经是代理,则直接使用代理 |
| computed
| function
| { value: ... }
| 当读取value值时, 会根据情况决定是否要运行函数 |
应用:
- 如果想要让一个对象变为响应式数据,可以使用
reactive
或ref
- 如果想要让一个对象的所有属性只读,使用
readonly
- 如果想要让一个非对象数据变为响应式数据,使用
ref
- 如果想要根据已知的响应式数据得到一个新的响应式数据,使用
computed
响应式数据判断 {#响应式数据判断}
| API | 含义 |
|--------------|-----------------------------------------------------------------------------------------|
| isProxy
| 判断某个数据是否是由reactive
或readonly
|
| isReactive
| 判断某个数据是否是通过reactive
创建的 详细:https://v3.vuejs.org/api/basic-reactivity.html#isreactive |
| isReadonly
| 判断某个数据是否是通过readonly
创建的 |
| isRef
| 判断某个数据是否是一个ref
对象 |
响应式数据的转化 {#响应式数据的转化}
| API | 含义 |
|----------|------------------------------------------------|
| unref
| 等同于:isRef(val) ? val.value : val
|
| toRef
| 得到一个响应式对象某个属性的ref格式 |
| toRefs
| 把一个响应式对象的所有属性转换为ref格式,然后包装到一个plain-object
中返回 |
数据监听 {#数据监听}
watchEffect
|---------------------|---------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| const stop = watchEffect(() => { // 该函数会立即执行,然后追中函数中用到的响应式数据,响应式数据变化后会再次执行 }) // 通过调用stop函数,会停止监听 stop(); // 停止监听
|
watch
|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // 等效于vue2的$watch // 监听单个数据的变化 const state = reactive({ count: 0 }) watch(() => state.count, (newValue, oldValue) => { // ... }, options) const countRef = ref(0); watch(countRef, (newValue, oldValue) => { // ... }, options) // 监听多个数据的变化 watch([() => state.count, countRef], ([new1, new2], [old1, old2]) => { // ... });
|
注意:无论是watchEffect
还是watch
,当依赖项变化时,回调函数的运行都是异步的(微队列)
应用:除非遇到下面的场景,否则均建议选择watchEffect
- 不希望回调函数一开始就执行
- 数据改变时,需要参考旧值
- 需要监控一些回调函数中不会用到的数据
生命周期函数 {#生命周期函数}
| vue2 option api | vue3 option api | vue 3 composition api | |-----------------|----------------------|-----------------------| | beforeCreate | beforeCreate | 不再需要,代码可直接置于setup中 | | created | created | 不再需要,代码可直接置于setup中 | | beforeMount | beforeMount | onBeforeMount | | mounted | mounted | onMounted | | beforeUpdate | beforeUpdate | onBeforeUpdate | | updated | updated | onUpdated | | beforeDestroy | ==改== beforeUnmount | onBeforeUnmount | | destroyed | ==改==unmounted | onUnmounted | | errorCaptured | errorCaptured | onErrorCaptured | | - | ==新==renderTracked | onRenderTracked | | - | ==新==renderTriggered | onRenderTriggered |
新增钩子函数说明:
| 钩子函数 | 参数 | 执行时机 | |-----------------|---------------|------------------| | renderTracked | DebuggerEvent | 渲染vdom收集到的每一次依赖时 | | renderTriggered | DebuggerEvent | 某个依赖变化导致组件重新渲染时 |
DebuggerEvent:
- target: 跟踪或触发渲染的对象
- key: 跟踪或触发渲染的属性
- type: 跟踪或触发渲染的方式
vue-router的使用 {#vue-router的使用}
应该很好理解吧orz
|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| import {useRoute, useRouter} from "vue-router"; export default defineComponent({ name: 'Home', setup() { // 相当于this.$router let router = useRouter(); // 相当于this.$route let route = useRoute(); } });
|
vuex的使用 {#vuex的使用}
应该很好理解吧orz
|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import {createStore} from 'vuex' export default createStore({ state: { name: "sena" }, mutations: { updateName(store, payload) { store.name = payload; } }, actions: {}, modules: {} })
|
|------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <div class="home"> <input type="text" v-model="msg"> <button @click="showState">点我获取state</button> <button @click="initState">初始化state</button> </div> </template> <script lang="ts"> import {defineComponent, ref, watchEffect, reactive} from 'vue'; import {useStore} from "vuex"; export default defineComponent({ name: 'Home', setup() { let store = useStore(); let msgRef = ref(store.state.name); watchEffect(() => { // msg更新时保存值到vuex中 store.commit("updateName", msgRef.value) }); watchEffect(() => { // vuex中的值更新时更新msg msgRef.value = store.state.name; }) return { msg : msgRef, showState() { console.log(store.state) }, initState() { store.commit("updateName", "") }, } } }); </script>
|