Vue3 官网:https://v3.cn.vuejs.org/
Install CDN 引入:<script src="https://unpkg.com/vue@next"></script>
静态引入:<script src="js/vue.js"></script>
Counter 计数器 js 原生实现:
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Document</title > </head > <body > <h2 class ="counter" > 0</h2 > <button class ="increment" > +1</button > <button class ="decrement" > -1</button > </body > <script > const counterEl = document .querySelector (".counter" ); const incrementEl = document .querySelector (".increment" ); const decrementEl = document .querySelector (".decrement" ); let counter = 0 ; counterEl.innerHTML = counter; incrementEl.addEventListener ("click" , () => { counter++; counterEl.innerHTML = counter; }); decrementEl.addEventListener ("click" , () => { counter--; counterEl.innerHTML = counter; }); </script > </html >
vue 实现:
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 39 40 41 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > Document</title > </head > <script src ="js/vue.js" > </script > <body > <div id ="app" > </div > </body > <script > Vue.createApp({ template: ` <div > <h1 > {{message }} </h1 > <h2 > {{counter }} </h2 > <button @click ="increment" > +1</button > <button @click ="decrement" > -1</button > </div > `, // vue3 中 data 必须是一个函数 data: function () { return { counter: 0, message: "Hello World", }; }, methods: { increment() { this.counter++; }, decrement() { this.counter--; }, }, }).mount("#app"); </script > </html >
Vue基础 methods 中函数不能使用箭头函数,如果箭头函数,箭头函数里面的 this 就指向了 window。
vscode 生成代码片段:文件
->首选项
->用户片段
->输入 html
,打开 Vscode代码片段生成 复制进去即可。
Mustche 语法 即双括号语法,其中可以写属性名、表达式、函数调用、三元运算符。不能写语句(赋值,循环等)。
v-bind v-bind 用于绑定属性,简写为 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <body > <div id ="app" > </div > <template id ="my-app" > <img v-bind:src ="imgUrl" alt ="" > <a href ="aUrl" > 百度一下</a > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ){ return { imgUrl : "https://github.githubassets.com/images/modules/profile/badge--acv-64.png" , aUrl : "https://www.baidu.com" } } } Vue .createApp (App ).mount ("#app" ); </script >
动态绑定 class
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 <style > .active { background-color : pink; } </style > <body > <div id ="app" > </div > <template id ="my-app" > <div :class ="{active: isActive}" > 嘻嘻嘻</div > <div :class ="classGroup" > 哈哈哈</div > <button @click ="toggle" > 转换</button > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ){ return { isActive : true , classGroup : ['active' , 'title' , 'abc' ] } }, methods :{ toggle ( ){ this .isActive = !this .isActive } } } Vue .createApp (App ).mount ("#app" ); </script >
动态绑定 style
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 <body > <div id ="app" > </div > <template id ="my-app" > <div :style ="myStyle" > 嘻嘻嘻</div > <div :style ="{'font-size': '100px', color: 'red'}" > 嘻嘻嘻</div > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ){ return { myStyle : { fontSize : "24px" , fontWeight : 700 } } } } Vue .createApp (App ).mount ("#app" ); </script >
动态绑定属性,此处的 name 也根据 data 声明来决定
1 2 3 4 5 6 <div : [name ]="value" > 哈哈</div > data(){ name: 'abc', value: 'cdf' }
v-bind 直接绑定对象,如下会生成 <div name=”ReaJason” age=”18”>哈哈</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 <body > <div id ="app" > </div > <template id ="my-app" > <div v-bind ="info" > 哈哈</div > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ){ return { info : { name : "ReaJason" , age : 18 } } } } Vue .createApp (App ).mount ("#app" ); </script >
v-on v-on 绑定事件,简写为 @
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 <body > <div id ="app" > </div > <template id ="my-app" > <button v-on:click ="btnClick" > {{counter}}</button > <button @click ="btnClick" > 按钮</button > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ){ return { counter : 0 } }, methods : { btnClick ( ){ this .counter ++; } } } Vue .createApp (App ).mount ("#app" ); </script >
v-on 传递参数
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 <body > <div id ="app" > </div > <template id ="my-app" > <button @click ="btnClick" > 按钮1</button > <button @click ="btnClick1($event, '18')" > 按钮2</button > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { counter : 0 } }, methods : { btnClick (event ) { console .log (event); }, btnClick1 (event, age ) { console .log ("==========" ); console .log (event); console .log (age); } } } Vue .createApp (App ).mount ("#app" ); </script >
v-on 使用修饰符
.stop:阻止事件冒泡
@keyup.enter:绑定回车键,可自定义和其他键位
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 <body > <div id ="app" > </div > <template id ="my-app" > <div @click ="btnClick" > <button @click.stop ="btnClick1" > 按钮</button > </div > <input type ="text" value ="" @keyup.enter ="printV" > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { counter : 0 } }, methods : { btnClick ( ) { console .log ("btnClick" ); }, btnClick1 ( ) { console .log ("btnClick1" ); }, printV (event ){ console .log (event.target .value ); } } } Vue .createApp (App ).mount ("#app" ); </script >
v-if v-if 是惰性的,条件为 false,不会渲染,条件为 true 才能看见
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 <body > <div id ="app" > </div > <template id ="my-app" > <h2 v-if ="score > 90" > 优秀</h2 > <h2 v-else-if ="score > 60" > 良好</h2 > <h2 v-else > 不及格</h2 > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { score : 61 } }, methods : { } } Vue .createApp (App ).mount ("#app" ); </script >
v-show v-show 为 false 时,相当于给元素添加了 display=none 属性
1 <p style ="display: none;" > 嘻嘻嘻</p >
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 <body > <div id ="app" > </div > <template id ="my-app" > <p v-if ="isShow" > 哈哈哈</p > <p v-show ="isShow" > 嘻嘻嘻</p > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { isShow : false } }, methods : { } } Vue .createApp (App ).mount ("#app" ); </script >
v-for 列表渲染,支持数组、对象、数字,可以是用 in 也可以用 of。
当数组使用更新数组的方法会改变原来的值会更新视图,而生成新数组的方法不会更新回原数组,如 filter、concat、slice。
v-for 中的 key 是为了高效进行数组渲染时候对数组中多余或需要添加的元素的修改。
没有 key,vue 会尽可能复用原先的 VNode 节点填充,最后进行添加和删除节点
有 key 时,会先从前进行比对,再从后往前比对,中间使用 map 进行比对,再决定需要删除和添加的元素
对于 Vue 来说,html 会转换为 VNode Tree,再渲染到页面上。
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <body > <div id ="app" > </div > <template id ="my-app" > <h3 > 遍历数组:一个参数</h3 > <ul > <li v-for ="movie in movies" :key ="movie" > {{movie}}</li > </ul > <h3 > 遍历数组:两个参数</h3 > <ul > <li v-for ="(movie,index) in movies" :key ="movie" > {{index}}---{{movie}}</li > </ul > <h3 > 遍历对象:一个参数</h3 > <ul > <li v-for ="value in info" :key ="value" > {{value}}</li > </ul > <h3 > 遍历对象:两个参数</h3 > <ul > <li v-for ="(value,key) in info" :key ="key" > {{key}}---{{value}}</li > </ul > <h3 > 遍历对象:三个参数</h3 > <ul > <li v-for ="(value,key,index) in info" :key ="key" > {{index}}---{{key}}---{{value}}</li > </ul > <h3 > 遍历数字:一个参数</h3 > <ul > <li v-for ="value in 4" :key ="value" > {{value}}</li > </ul > <h3 > 遍历数字:两个参数</h3 > <ul > <li v-for ="(value,index) in 4" :key ="value" > {{index}}---{{value}}</li > </ul > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { movies : ["朝花夕誓" ,"烟火" ,"你的名字" ], info : {name : "ReaJason" , gender : "male" , age : 18 } } }, methods : { } } Vue .createApp (App ).mount ("#app" ); </script >
computed 计算属性,计算属性是有缓存的,状态改变时只会计算一次,而 methods 每次都会当函数进行调用
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 39 40 41 42 43 <body > <div id ="app" > </div > <template id ="my-app" > <h2 > {{fullName}}</h2 > <h2 > {{fullName}}</h2 > <h2 > {{fullName}}</h2 > <h2 > {{fullName}}</h2 > <h2 > {{getFullName()}}</h2 > <h2 > {{getFullName()}}</h2 > <h2 > {{getFullName()}}</h2 > <h2 > {{getFullName()}}</h2 > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { firstName : "Tom" , lastName : "Jason" } }, methods : { getFullName ( ){ console .log ("方法调用了" ) return this .firstName + " " + this .lastName } }, computed : { fullName ( ){ console .log ("计算属性调用了" ); return this .firstName + " " + this .lastName } } } Vue .createApp (App ).mount ("#app" ); </script >
计算属性的 get 和 set,如果单函数就是 get 方法,如果需要加 set 方法使用对象即可
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 39 40 41 <body > <div id ="app" > </div > <template id ="my-app" > <h2 > {{fullName}}</h2 > <button @click ="changeFullName" > change</button > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { firstName : "Tom" , lastName : "Jason" } }, methods : { changeFullName ( ){ this .fullName = "Code Why" } }, computed : { fullName : { get : function ( ){ return this .firstName + " " + this .lastName }, set : function (value ){ var str = value.split (" " ) this .firstName = str[0 ] this .lastName = str[1 ] } } } } Vue .createApp (App ).mount ("#app" ); </script >
watch 侦听器,侦听数据的变化自动调用
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 <body > <div id ="app" > </div > <template id ="my-app" > <input type ="text" v-model ="message" > <br > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { message : "hahah" } }, methods : { }, watch : { message (newValue, oldValue ){ console .log ("新值:" , newValue, "旧值:" , oldValue); } } } Vue .createApp (App ).mount ("#app" ); </script >
默认只能侦听属性本身,无法侦听到内部数据的改变
deep:true,开启深度侦听,可侦听内部属性的变化,无论多深
immediate:true,立即执行,无论数据是否改变都会执行一次
由于对象是引用类型,因此 oldValue 和 newValue 会执行同一个对象导致打印同一个值
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 39 40 41 42 43 44 45 46 47 48 <body > <div id ="app" > </div > <template id ="my-app" > <h2 > name:{{info.name}}</h2 > <h2 > age:{{info.name}}</h2 > <h2 > friend.name:{{info.friend[0].name}}</h2 > <button @click ="changeInfo" > 改变info</button > <button @click ="changeName" > 改变info的name</button > <button @click ="changeFriendName" > 改变info的friend中的name</button > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { info : { name : "Tom" , age : 18 , friend : [{ name : "lucy" , age : 18 }] } } }, methods : { changeInfo ( ) { this .info = { name : "Mory" , age : 20 , friend : [{ name : "Jack" , age : 20 }] } }, changeName ( ) { this .info .name = "Jery" }, changeFriendName ( ) { this .info .friend [0 ].name = "lily" } }, watch : { info : { handler : function (newValue, oldValue ) { console .log ("新值:" , newValue); console .log ("旧值:" , oldValue); }, deep : true , immediate : true } } } Vue .createApp (App ).mount ("#app" ); </script >
购物车案例 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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > 购物车案例</title > </head > <style > #app { display : flex; flex-direction : column; justify-content : center; align-items : center; margin : 0px auto; } table { border : 1px solid #e9e9e9 ; border-collapse : collapse; border-spacing : 0 ; } th , td { padding : 8px 16px ; border : 1px solid #e9e9e9 ; text-align : center; } th { background-color : #f7f7f7 ; color : #5c6b77 ; font-weight : 600 ; } </style > <body > <div id ="app" > </div > <template id ="my-app" > <template v-if ="books.length > 0" > <h3 > {{title}}</h3 > <div class ="priceBox" > <span class ="toP" > 总价格:{{totalPrice}}</span > </div > <table > <thead > <tr > <th > </th > <th > 书名</th > <th > 时间</th > <th > 价格</th > <th > 数量</th > <th > 操作</th > </tr > </thead > <tbody > <tr v-for ="(book, index) of books" > <td > {{book.id}}</td > <td > {{book.name}}</td > <td > {{book.time}}</td > <td > {{book.price}}</td > <td > <button @click ="decreCount(index)" > -1</button > <span style ="margin: 0 5px;" > {{book.count}}</span > <button @click ="increCount(index)" > +1</button > </td > <td > <button @click ="deleteBook(index)" > 移除</button > </td > </tr > </tbody > </table > </template > <template v-else > <h2 > 购物车为空</h2 > </template > </template > </body > <script src ="https://cdn.bootcdn.net/ajax/libs/vue/3.1.5/vue.global.min.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { title : "购物车案例" , books : [ { id : 1 , name : "算法导论" , time : "2020-01" , count : 1 , price : 80 }, { id : 2 , name : "设计模式" , time : "2021-01" , count : 1 , price : 60 }, { id : 3 , name : "CS61B" , time : "2020-04" , count : 1 , price : 100 }, { id : 4 , name : "JavaScript百炼成仙" , time : "2020-02" , count : 1 , price : 34 }, ] } }, computed : { totalPrice ( ){ let finalPrice = 0 ; for (var book of this .books ){ finalPrice += book.price * book.count } return finalPrice } }, methods : { decreCount (index ) { this .books [index].count -- }, increCount (index ) { this .books [index].count ++ }, deleteBook (index ) { this .books .splice (index, 1 ) } } } Vue .createApp (App ).mount ("#app" ); </script > </html >
v-model v-model 可以在表单 input、textarea 以及 select 等元素上创建双向数据绑定,实质就是语法糖,将 data 中的数据绑定到表单元素中,同时监听表单元素的更新,同步更新到 data 中
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 39 40 41 42 43 44 45 46 47 48 49 <body > <div id ="app" > </div > <template id ="my-app" > <form action ="#" method ="post" > 普通输入框:<input type ="text" v-model ="text" >   {{text}} <br > 单选复选框:<input type ="checkbox" v-model ="singleCheckBox" value ="agree" > 同意协议 {{singleCheckBox}}<br > 多选复选框:<input type ="checkbox" v-model ="multiChcekBox" value ="篮球" > 篮球 <input type ="checkbox" v-model ="multiChcekBox" value ="足球" > 足球 {{multiChcekBox}}<br > 单选按钮:<input type ="radio" v-model ="gender" value ="female" > 女 <input type ="radio" v-model ="gender" value ="male" > 男 {{gender}}<br > 下拉框:<select v-model ="point" multiple > <option value ="打游戏" > 打游戏</option > <option value ="看电影" > 看电影</option > <option value ="逛街" > 逛街</option > <option value ="抓娃娃" > 抓娃娃</option > </select > {{point}} <br > 文本域:<textarea v-model ="body" cols ="10" rows ="5" > </textarea > {{body}} <br > </form > </template > </body > <script src ="../js/vue.js" > </script > <script > const App = { template : "#my-app" , data ( ) { return { text : "Hello World" , singleCheckBox : "" , multiChcekBox : [], gender :"" , point : "" , body :"你好你好你好" } }, methods : { } } Vue .createApp (App ).mount ("#app" ); </script >
默认监听 input 框的 change 事件,使用 .lazy 修饰符,监听 change 事件
v-model 绑定的数据总为 string,如果需要为数字类型,使用 .number 修饰符
.trim 为自动为数据去除前后空白字符
组件化开发 全局组件,所有组件中都能使用当前组件,使用 app.component 注册的组件为全局组件
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 39 40 41 42 <body > <div id ="app" > </div > <template id ="my-app" > <gl > </gl > <gl > </gl > <gl > </gl > </template > <template id ="global" > <h2 > {{message}}</h2 > </template > </body > <script src ="../js/vue.js" > </script > <script > const gl = { template : "#global" , data ( ){ return { message : "Hello 全局组件" } } } const App = { template : "#my-app" , data ( ) { return { } }, methods : { } } const app = Vue .createApp (App ) app.component ("gl" , gl); app.mount ("#app" ); </script >
局部组件,只有注册的组件才能使用,在组件内部使用 compoennts 注册的组件为局部组件
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <body > <div id ="app" > </div > <template id ="my-app" > <tel1 > </tel1 > <tel2 > </tel2 > </template > <template id ="tel2" > <h2 > {{message}}</h2 > </template > <template id ="tel1" > <h2 > {{message}}</h2 > </template > </body > <script src ="../js/vue.js" > </script > <script > const tel1 = { template : "#tel1" , data ( ){ return { message : "Hello tel1" } } } const tel2 = { template : "#tel2" , data ( ) { return { message : "Hello tel2" } } } const App = { template : "#my-app" , components :{ tel1, tel2 }, data ( ) { return { } }, methods : { } } const app = Vue .createApp (App ) app.mount ("#app" ); </script >
webpack webpack是一个静态的模块化打包工具,为现代的 JavaScript 应用程序。
webpack 能将各种各样的前端模块化开发格式文件,转为 js,html,css,以及静态资源
安装 webpack:
1 2 npm install webpack webpack-cli –g npm install webpack webpack-cli –D
通常使用局部 webpack 来管理项目文件,而项目文件通过 package.json 来进行依赖管理,使用 npm init
即可生成当前项目的 package.json 文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "name" : "1" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , "scripts" : { "build" : "webpack" } , "author" : "" , "license" : "ISC" , "devDependencies" : { "webpack" : "^5.64.4" , "webpack-cli" : "^4.9.1" } }
使用局部 webpack 需要使用 npx webpack
,在 package.json 中添加脚本之后,使用 nom run build
即可使用局部 webpack 打包
配置文件 配置文件名为:webpack.config.json
,可以使用其他名字,但是使用 webpack 时需要指定配置文件
webpack 会从入口文件,生成依赖树,只有依赖的文件才会打包进去
1 2 3 4 5 6 7 8 9 const path = require ("path" );module .exports = { entry : "./src/main.js" , output : { filename : "bundle.js" , path : path.resolve (__dirname, "./dist" ) } }
css-loader webpack 默认只会解析 js 文件,其他文件都需要 loader 支持,css-loader 支持解析 css 文件
安装 css-loader:npm install css-loader -D
loader 配置方式,module.rules:
test 属性:用于对 resource 进行匹配,通常设置为正则表达式
use 属性:
loader:required,loader 加载器的字符串
options:optional,值会传入 loader 中
user:[“style-loader”] 是 user:[{loader:”style-loader”}]
loader 属性:Rule.user:[loader] 的缩写
css-loader 只用于解析 css 文件,而不会加载样式,style-loader 会完成插入 style 的操作
安装 style-loader:npm install style-loader -D
处理 less 安装:npm install less-loader -D
loader 执行顺序是从右至左(从下到上或从后往前)
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 const path = require ("path" );module .exports = { entry : "./src/main.js" , output : { filename : "bundle.js" , path : path.resolve (__dirname, "./dist" ) }, module : { rules : [ { test : /\.css$/ , use : [ { loader : "style-loader" }, { loader : "css-loader" }, ] }, { test : /\.less$/ , use : [ { loader : "style-loader" }, { loader : "css-loader" }, { loader : "less-loader" } ] } ] } }
PostCSS PostCSS 是一个通过 JavaScript 来转换样式的工具,可进行 CSS 的转换和适配,比如自动添加浏览器前缀、css 样式重置等
安装 postcss、postcss-cli:npm install postcss postcss-cli -D
安装 autoprefixer 插件:npm install autoprefixer -D
直接使用 postcss 并使用插件:npx postcss --use autoprefixer -o end.css ./src/css/style.css
安装 postcss-loader:npm install postcss-loader -D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { test : /\.css$/ , use : [ { loader : "style-loader" }, { loader : "css-loader" }, { loader : "postcss-loader" , options : { postcssOptions : { plugins : [ require ("autoprefixer" ) ] } } } ] }
使用单独配置:postcss.config.js
1 2 3 4 5 module .exports = { plugins : [ require ("autoprefixer" ) ] }
postcss-preset-env 更加强大,它能将现代 CSS 特性转成大多数浏览器认识的 CSS,并内置了 autoprefixer。
安装 postcss-preset-env :npm install postcss-preset-env -D
1 2 3 4 5 6 module .exports = { plugins : [ "postcss-preset-env" ] }
打包图片 安装 file-loader:npm install file-loader -D
1 2 3 4 5 6 7 8 9 10 { test :/\.(jpg|png|gif)$/ , use : { loader : "file-loader" , options :{ outputPath : "img" , name : "[name]_[hash:6].[ext]" } } }
url-loader 可以将较小的文件转为 base64 的 URI,
安装 url-loader:npm install url-loader -D
1 2 3 4 5 6 7 8 9 10 { test :/\.(jpg|png|gif)$/ , use : { loader : "url-loader" , options :{ name : "img/[name]_[hash:6].[ext]" , limit : 100 * 1024 } } }
asset module type 资源模板类型:webpack5 不需要下载 loader
asset/resource 对应 file-loader
asset/inline 对应 url-loader
asset 由 webpack 决定使用哪种 loader
1 2 3 4 5 6 7 8 9 10 11 12 { test :/\.(jpg|png|gif)$/ , type : "asset" , generator : { filename : "img/[name]_[hash:6][ext]" }, parser : { dataUrlCondition :{ maxSize : 100 * 1024 } } }
字体文件 ……待学习
组件化开发 父子组件通信
Tab栏制作 App.vue
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> <tab-bar :titles="title" @change="change"></tab-bar> <h2>{{contents[curIndex]}}</h2> </div> </template> <script> import TabBar from "./TabBar.vue"; export default { components: { TabBar, }, data() { return { curIndex: 0, title: ["衣服","裤子","鞋子"], contents: ["衣服页面","裤子页面","鞋子页面"] }; }, methods:{ change(index){ this.curIndex = index } } }; </script>
TabBar.vue
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <template> <div class="tab-control"> <div class="tab-control-item" :class="{ active: curIndex === index }" v-for="(title, index) in titles" :key="title" @click="itemClick(index)" > <span>{{ title }}</span> </div> </div> </template> <script> export default { props: { titles: { type: Array, default() { return []; }, } }, data() { return { curIndex: 0, }; }, emits:["change"], methods: { itemClick(index) { this.curIndex = index; this.$emit("change", index) }, }, }; </script> <style scoped> .tab-control { display: flex; justify-content: space-between; } .tab-control-item.active { color: red; } .tab-control-item.active span { border-bottom: 3px solid red; } </style>
非父子组件通信 Provide和Inject:
父组件通过 provide 为组件树的所有子组件提供数据
子组件通过 inject 来获取组件树上父组件使用 provide 传递的数据
1 2 3 4 5 6 7 8 9 10 11 provide ( ){ return { msg : "Hello My Son" , count : 10 } }inject : ["msg" , "count" ]
全局事件总线 mitt:
可全局发出和监听事件
发出事件:emitter.emit("why", {name: "why", age: "18"})
监听事件:emitter.on("why", (info) -> console.log(info))
取消所有监听:emitter.all.clear
插槽 slot:
子组件使用 slot 标签定义插槽,父组件中使用子组件时,在子组件中使用其他标签,会被插入到子组件的插槽中
匿名插槽:即 <slot></slot>
,默认名字为 default,如果有多个匿名插槽,所有插槽都会渲染一遍父组件传来的标签
具名插槽:即 <slot name="why"></slot>
,父组件中使用 v-slot:name
可指定名字插入到子组件的哪个插槽
v-slot:name
可缩写为 #name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <div class ="left" > <slot name ="left" > </slot > </div > <div class ="center" > <slot name ="center" > </slot > </div > <div class ="right" > <slot name ="right" > </slot > </div > <tab-bar > <template #left > <div > 左边来点啥</div > </template > <template v-slot:center > <div > 中间来点啥</div > </template > <template v-slot:right > <div > 右边来点啥</div > </template > </tab-bar >
动态插槽名使用:v-slot:[name]
作用域插槽:父组件使用插槽传来的值,v-slot="slotProps"
动态组件:使用 component 组件,通过 is 实现,<component :is="currentTabComponent"></component>
保存组件的状态:使用 keep-alive 包裹需要缓存数据的组件即可,默认都可以缓存
include:只有名称匹配的组件才会被缓存
exclude:任何名称匹配的组件都不被缓存
max:最多缓存的组件数
include 和 exclude 使用逗号分隔字符串、正则表达式和数组
异步组件:定义路由时一般使用的就是异步组件,异步组件是为了 webpack 的分包
1 2 3 4 5 components : { AsyncComponent : defineAsyncComponent (() => import ('./components/AsyncComponent.vue' )) }
Suspense:加载状态将由 <Suspense>
控制,组件自身的加载、错误、延迟和超时选项都将被忽略
default:需要记载的异步组件
fallback:加载错误的时候显示 fallback 插槽的内容
1 2 3 4 5 6 7 8 <suspense > <template #default > <async-home > </async-home > </template > <template #fallback > <loading > </loading > </template > </suspense >
$refs:用来操作 DOM 节点,DOM 定义 ref 属性,都会加入到实例的 $refs 中
$parent:获取父组件实例
$root:获取根组件实例
$el:获取 DOM 节点
生命周期
func
desc
beforeCreate
组件实例创建之前
created
组件实例创建之后
beforeMount
挂载之前
mounted
挂载完成
beforeUpdate
数据变化界面刷新之前
updated
数据刷新之后
beforeUnmount
组件销毁取消挂载之前
unmonted
组件销毁移除之后
activated
组件活跃时
deactivated
组件缓存后
组件 v-modal 子组件中使用 v-modal 相当于:
modalValue 属性传给了子组件
子组件使用 update:modalValue 传递给事件给父组件
自定义多个 v-modal:<nav-bar v-modal="message" v-modal:title="title"></nav-bar>
props: [“modalValue”,”title”]
emits: [“update:modalValue”, “update:title”]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export default { props : ["modelValue" ], emits : ["update:modelValue" ], computed : { value : { get ( ){ return this .modelValue }, set (value ){ this .$emit("update:modelValue" , value) } } } }
过渡与动画 使用 transition 标签包裹需要使用过渡的标签或组件即可
v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter-from 被移除),在过渡/动画完成之后移除。
v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave-from 被移除),在过渡/动画完成之后移除。
使用 css 动画需要使用 animation ,@keyprame
过渡模式 mode:out-in 常用
in-out
: 新元素先进行进入过渡,完成之后当前元素过渡离开。
out-in
: 当前元素先进行离开过渡,完成之后新元素过渡进入。
animate.css 使用自定义过渡类完成动画:
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
安装:npm install animate.css
引入:import 'animate.css'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <template> <div> <button @click="show = !show">转换</button> <transition name="custom-classes-transition" enter-active-class="animate__animated animate__tada" leave-active-class="animate__animated animate__bounceOutRight" > <p v-if="show">hello</p> </transition> </div> </template> <script> export default { data() { return { show: true, }; }, }; </script>
gsap 使用 js 钩子函数完成动画:
1 2 3 4 5 6 7 8 9 10 11 12 13 <transition @before-enter ="beforeEnter" @enter ="enter" @after-enter ="afterEnter" @enter-cancelled ="enterCancelled" @before-leave ="beforeLeave" @leave ="leave" @after-leave ="afterLeave" @leave-cancelled ="leaveCancelled" :css ="false" > </transition >
安装:npm install gsap
引入:import gsap from 'gsap'
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> <input type="num" step="100" v-model="counter" /> <h2>{{ showValue.toFixed(0) }}</h2> </div> </template> <script> import gsap from "gsap"; export default { data() { return { show: true, counter: 0, showValue: 0, }; }, watch: { counter(newValue) { gsap.to(this, { duration: 1, showValue: newValue }); }, }, }; </script>
列表过渡
使用 transition-group 包裹列表渲染
元素动画使用 name 属性定义 css 动画
移动使用 name 属性的 move
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <template> <div> <button @click="add">Add</button> <button @click="remove">Remove</button> <button @click="shuffle">Shuffle</button> <transition-group name="list" tag="p"> <span v-for="num in nums" :key="num" class="list-item"> {{ num }} </span> </transition-group> </div> </template> <script> import _ from "lodash"; export default { data() { return { nums: [1, 2, 3, 4, 5, 6, 7], count: 10, }; }, methods: { randomIndex() { return Math.floor(Math.random() * this.nums.length); }, add() { this.nums.splice(this.randomIndex(), 0, this.count++); }, remove() { this.nums.splice(this.randomIndex(), 1); }, shuffle() { this.nums = _.shuffle(this.nums); }, }, }; </script> <style scoped> .list-item { display: inline-block; margin: 0 5px; } .list-enter-active, .list-leave-active { transition: all 1s ease; } .list-move { transition: transform 0.8s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateY(30px); } .list-leave-active { position: absolute; } </style>
Mixin Mixin 能分发组件中可复用的功能,使用 mixins:[mixin1] 接收,当其中的属性相同时会产生冲突:
data 函数中返回的对象
生命周期钩子函数
其他都会进行合并
全局混入:app.mixin()
extends:继承组件的对象属性
Composition API setup 函数 无法使用 this,原因是 setup 没有绑定组件实例,setup 执行时,data,components,methods 都还没执行
参数:
props,父组件所传过来的属性
context,SetupContext 上下文对象
attrs,非 props 的属性
slots,插槽
emit,setup 中没有 this,只能使用 emit 去取代它发送事件
返回值:返回一个对象,返回值可在 template 中使用,返回的属性不具有响应式,想要响应式需要对应函数包裹属性
基础 reactive():传入对象和数组
ref():传入基本数据类型,取值需要 .value,template 中则使用不需要 .value
readonly():属性只读无法修改
isProxy,检查对象是否是 reactive 或 readonly 创建的 proxy
isReactive,检查对象是否是 reactive 创建的
isReadonly,检查对象是否是 readonly 创建的
shallowReactive,不执行深层的响应
shallowReadonly,不执行深层的只读
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 <template> <div> <h2>计数:{{ counter }}</h2> <button @click="increment">+1</button> <h2>信息:{{ info.uname }}</h2> <input type="text" v-model="info.uname" /> </div> </template> <script> import { reactive, ref } from "vue"; export default { setup(props, context) { const counter = ref(0); const increment = () => { counter.value++; }; const info = reactive({ uname: "ReaJason", age: 20, }); return { counter, increment, info, }; }, }; </script>
toRefs(),传入 reactive 对象,使其解构出来的仍具有响应式
toRef(reactive, “name”),传入 reactive 对象,指定其中 name 属性仍具有响应式并返回
unref,传入一个对象,如果是 ref 返回值,如果不是直接返回原对象
isRef,判断对象是否是 ref 对象
shallowRef,创建浅层的 ref 对象
triggerRef,手动触发 shallowRef 相关联的副作用
customRef,自定义 ref,对其自定义跟踪和更新触发,track,trigger
computed,计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 import { computed, ref } from "vue" ;export default { setup ( ) { const firstName = ref ("Rea" ); const lastName = ref ("Jason" ); const fullName = computed (() => firstName.value + " " + lastName.value ); return { fullName }; }, };
provide(name,value),为子组件及其以下的组件提供数据
inject(name,default),获取父组件链的数据
侦听数据变化 watchEffect:自动收集依赖,并且会立即执行一次
停止侦听:watchEffect 返回一个函数,调用这个函数则会停止侦听
清除副作用:watchEffect 中的箭头函数接收一个参数 onInvalidate
使用 ref 获取 dom 节点,并调整 watchEffect 执行时机
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> <h2 ref="title">哈哈哈</h2> </div> </template> <script> import { ref, watchEffect } from "vue"; export default { setup() { const title = ref(null); watchEffect( () => { console.log(title.value); }, { flush: "post" } // 默认是 pre,如果需要操作 dom 节点需要设置 post,不然第一次为 null ); return { title, }; }, }; </script>
watch:手动指定侦听的 属性,可获取状态前后的值,等同于 options api 的 watch
watch 支持侦听,getter 函数,ref 对象,数组
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 39 40 41 42 43 44 45 46 47 48 49 <template> <div> <h2>{{ info.name }}</h2> <button @click="changeData">change</button> <h2>{{ count }}</h2> <button @click="changeCount">+1</button> </div> </template> <script> import { reactive, ref, watch } from "vue"; export default { setup() { const info = reactive({ name: "Rea", age: "20" }); // 侦听,传入 getter 函数 watch( () => info.name, (newValue, oldValue) => { console.log(newValue + " " + oldValue); } ); const count = ref(0); // 传入 ref 函数 watch( count, (newValue, oldValue) => { console.log(newValue + " " + oldValue); }, { immediate: true, deep: true } ); const changeCount = () => count.value++; const changeData = () => { info.name = "Jason"; }; return { info, changeData, changeCount, count, }; }, }; </script>
生命周期钩子 组件创建前和创建完成的生命周期直接在 setup 中书写即可
hook
desc
onBeforeMount(()=> {})
挂载前
onMounted(()=> {})
挂载完成
onBeforeUpdate(()=> {})
更新前
onUpdated(()=> {})
更新完成
onBeforeUnmount(()=> {})
卸载之前
onUnmounted(()=> {})
卸载完成之后
onActivated(()=> {})
活动时
onDeactivated(()=> {})
缓存时
VueRouter 安装:npm install vue-router@4
基本使用:
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 import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' const routes = [ { path : "/" , redirect : "/home" }, { path : '/home' , name : 'home' , component : Home }, { path : '/about' , component : () => import ('../views/About.vue' ) } ]const router = createRouter ({ history : createWebHistory (process.env .BASE_URL ), routes })export default router
1 2 3 4 5 6 7 <template> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <router-view/> </template>
<router-view />
是用来为渲染组件占位的
<router-link>
是用来指定路由去向
to 属性:字符串(path 的字符串)或对象
repalce 属性:点击调用 replace,默认是 push
active-class 属性:设置激活后应用的 class,默认为 router-link-active
exact-active-class 属性:链接精准激活时应用的 class,默认是 router-link-exact-active
动态路由匹配 路由设置 path:path: '/user/:username'
页面路由:<router-link *to*="/user/reajason">User</router-link>
获取值:所有组件都可以使用 this.$route.params 获取,setup 中使用 useRouter 返回的 route
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <h1>User :{{ $route.params .username }}</h1>import { useRoute } from "vue-router" ;export default { setup ( ) { const route = useRoute (); const username = route.params .username ; return { username, }; }, };
NotFound 配置 notfound 路由:
1 2 3 4 5 { path : '/:pathMatch(.*)*' , name : 'NotFound' , component : import ("../views/NotFound.vue" ) }
获取错误路径:
1 2 3 4 5 6 <template > <div > <h1 > 臣妾做不到</h1 > <h1 > {{$route.params.pathMatch}}</h1 > </div > </template >
嵌套路由 配置路由 children 属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { path : '/home' , component : Home , children : [ { path : "message" , component : import ('../views/HomeMessage.vue' ) }, { path : "other" , component : import ('../views/HomeOther.vue' ) } ] }
页面跳转:
1 2 3 4 5 6 7 8 9 <template > <div class ="home" > <h1 > This is an home page</h1 > <router-link to ="/home/message" > 消息</router-link > | <router-link to ="/home/other" > 其他</router-link > </div > <router-view /> </template >
编程式导航 使用 route 对象:
router.push
等同于 window.history.pushState
router.replace
等同于 window.history.replaceState
router.go
等同于 window.history.go
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 router.push ('/users/eduardo' ) router.push ({ path : '/users/eduardo' }) router.push ({ name : 'user' , params : { username : 'eduardo' } }) router.push ({ path : '/register' , query : { plan : 'private' } }) router.push ({ path : '/about' , hash : '#team' }) router.push ({ path : '/home' , replace : true }) router.replace ({ path : '/home' }) router.go (1 ) router.go (-1 ) router.go (3 ) router.go (-100 ) router.go (100 )
v-slot router-link 中的 v-slot 有如下对象:
href
:解析后的 URL。将会作为一个 <a>
元素的 href
属性。如果什么都没提供,则它会包含 base
。
route
:解析后的规范化的地址。
navigate
:触发导航的函数。 会在必要时自动阻止事件 ,和 router-link
一样。例如:ctrl
或者 cmd
+ 点击仍然会被 navigate
忽略。
isActive
:如果需要应用 active class ,则为 true
。允许应用一个任意的 class。
isExactActive
:如果需要应用 exact active class ,则为 true
。允许应用一个任意的 class。
1 2 3 4 5 6 7 8 9 <router-link to="/about" custom v-slot="{ href, route, navigate, isActive, isExactActive }" > <NavLink :active ="isActive" :href ="href" @click ="navigate" > {{ route.fullPath }} </NavLink > </router-link>
router-view 中 的 v-slot 有如下对象:
Component
: 要传递给 <component>
的 VNodes 是
prop。
route
: 解析出的标准化路由地址 。
1 2 3 4 5 6 7 8 9 10 <router-view v-slot ="{ Component, route }" > <transition :name ="route.meta.transition || 'fade'" mode ="out-in" > <keep-alive > <component :is ="Component" :key ="route.meta.usePathKey ? route.path : undefined" /> </keep-alive > </transition > </router-view >
动态路由 动态添加路由:
添加顶级路由,addRoute
1 router.addRoute ({ path : '/about' , component : About })
添加嵌套路由
1 router.addRoute ('admin' , { path : 'settings' , component : AdminSettings })
删除路由,
1 2 3 4 5 6 const removeRoute = router.addRoute (routeRecord)removeRoute () router.removeRoute ('about' )
导航守卫 在路由导航生命周期中进行回调。
导航被触发。
在失活的组件里调用 beforeRouteLeave
守卫。
调用全局的 beforeEach
守卫。
在重用的组件里调用 beforeRouteUpdate
守卫(2.2+)。
在路由配置里调用 beforeEnter
。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter
。
调用全局的 beforeResolve
守卫(2.5+)。
导航被确认。
调用全局的 afterEach
钩子。
触发 DOM 更新。
调用 beforeRouteEnter
守卫中传给 next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
beforeEach:前置路由
to:即将进入的目标
from:当前导航正要离开的路由
返回值 false,取消当前路由
返回值 路由地址,与 route.push 传入的参数一样
1 2 3 4 5 router.beforeEach ((to, from ) => { return false })
Vuex 安装:npm install vuex@next --save
简单使用,store 中存储数据,mutations 中提供修改数据的方法,全局组件都能访问 this.$store,commit 执行方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { createStore } from 'vuex' export default createStore ({ state : { counter : 0 }, mutations : { increment (state ) { state.counter ++ }, decrement (state ) { state.counter -- } } })
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <h2>这是一个数字:{{ $store.state.counter }}</h2> <button @click="increment">+1</button> <button @click="decrement">-1</button> </template> <script> export default { methods: { increment() { this.$store.commit("increment"); }, decrement() { this.$store.commit("decrement"); }, }, }; </script>
mapState computed 展开 state 中的所有属性:
1 2 3 4 5 6 7 8 9 10 11 ...mapState (["counter" ,"name" , "age" ]); ...mapState ({ sCounter : state => state.counter , sName : state => state.name , sAge : state => state.age })
setup 中使用 mapState:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const store = useStore ();const sCounter = computed (()=> store.state .counter )const storeStateFn = mapState (["counter" , "name" , "age" ]);const storeState = {}Object .keys (storeStateFn).forEach (fnKey => { const fn = storeStateFn[fnKey].bind ({$store : store}) storeState[fnKey] = computed (fn) })return { ...storeState };
getters 相当于 state 的计算属性,但是没有缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 state : { goods : [ {id : 1 , price : 30 , text : "java" }, {id : 2 , price : 40 , text : "js" }, {id : 3 , price : 20 , text : "html" }, ] }, getters : { expensiveBook : (state,getters )=> { return state.goods .filter (item => item.price > 30 ) }, getGreaterN : (state,getters ) => { return (n ) => state.goods .filter (item => item.price > n) } } } <h2>{{$store.getters .expensiveBook }}</h2><h2 > {{$store.getters.getGreaterN(20)}}</h2 >
mapGetters:
1 2 3 4 5 6 computed : { ...mapGetters ([ "expensiveBook" , "getGreaterN" ]) }
mutations 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。mutation 只能使用同步函数
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 39 40 41 42 43 44 45 46 47 48 mutations : { increment (state, payload ) { state.counter += payload }, decrement (state ) { state.counter -- } }methods : { increment ( ) { this .$store .commit ("increment" , 10 ); }, decrement ( ) { this .$store .commit ("decrement" ); }, }this .$store .commit ("increment" , {id : 1 , name : "ReaJason" });mutations : { increment (state, payload ) { state.counter += payload.value } }methods : { increment ( ) { this .$store .commit ({ type : "increment" , value : 10 }); } }const store = createStore ({ state : { ... }, mutations : { [SOME_MUTATION ] (state) { } } })
mapMutations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 export default { methods : { ...mapMutations ([ 'increment' , 'incrementBy' ]), ...mapMutations ({ add : 'increment' }) } }
actions Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
actions 中函数的 context 包含的参数:
commit,调用 mutation
dispatch,调用 action
getters,获取 getters 中属性
state,获取 state 中属性
rootGetters,获取父模块的 getters
rootState,获取父模块的 state
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 const store = createStore ({ state : { count : 0 }, mutations : { increment (state) { state.count ++ } }, actions : { increment (context) { context.commit ('increment' ) }, incrementAsync ({ commit }) { setTimeout (() => { commit ('increment' ) }, 1000 ) } } }) store.dispatch ('increment' ) store.dispatch ('incrementAsync' , { amount : 10 }) store.dispatch ({ type : 'incrementAsync' , amount : 10 })
mapActions,actions 中可以返回 Promise 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { mapActions } from 'vuex' export default { methods : { ...mapActions ([ 'increment' , 'incrementBy' ]), ...mapActions ({ add : 'increment' }) } }
module 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const moduleA = { state : () => ({ ... }), mutations : { ... }, actions : { ... }, getters : { ... } }const moduleB = { state : () => ({ ... }), mutations : { ... }, actions : { ... } }const store = createStore ({ modules : { a : moduleA, b : moduleB } }) store.state .a store.state .b
命名空间:模块默认是全局注册的,因此都会合并到主模块上
使用 namespace:true 隔离开
1 2 3 4 5 6 7 8 this .$store .getters ("/home/getName" ); this .$store .commit ("/home/changeName" )this .$store .dispatch ("/home/changeName" )
子模块调用父模块的 mutation 和 action
1 2 3 commit ('someMutation' , null , { root : true })dispatch ('someMutation' , null , { root : true })
createNamespacedHelpers 辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers ('some/nested/module' )export default { computed : { ...mapState ({ a : state => state.a , b : state => state.b }) }, methods : { ...mapActions ([ 'foo' , 'bar' ]) } }
后台管理项目