tips
开发单页面网页(spa)
html不区分大小写,vue中的使用驼峰驼峰命名的属性,用到html中大写字母一般会变为-
css中的id/class区分大小 ,支持驼峰命名
框架与库
框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。
库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
MVC与MVVM
引入Vue 1 2 3 4 5 <script src ="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" > </script > <script src ="https://cdn.jsdelivr.net/npm/vue" > </script >
插值表达式 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值 ,Mustache 标签将会被替代为对应数据对象上属性的值。无论何时,绑定的数据对象上属性发生了改变,插值处的内容都会更新(响应式的 )。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <body > <div id ="app" > message: {{message}} <br > number: {{number + 1}} <br > {{ true ? 'YES' : 'NO' }} </div > </body > <script src ="js/vue.js" > </script > <script > var vm = new Vue({ el: '#app' , data: { message: 'hello vuejs' , number: 1 } }) </script >
安装插件后需要手动勾选允许访问文件系统
html文件应用的vue.js,必须是开发版的
VueJs指令 v-cloak指令 用于解决插值表达式闪烁的问题(就是在页面渲染前,显示插值表达式的问题)
1 2 3 4 5 6 7 8 9 <style > [v-cloak] {display : none; } </style > <div id ="app" > <p v-cloak > message: {{message}} </p > </div >
v-text与v-html 用于渲染数据
v-text:将数据当作字符串显示在页面上
v-html:将数据当作html代码解析
注意:它们都会覆盖标签体中的内容
1 2 3 <div v-html ="message" > </div > <div v-text ="message" > </div >
v-on与修饰符 v-on指令用于事件绑定, 可简写成@
事件
keydown -> keypress -> keyup
mouseenter
事件修饰符
.stop 阻止事件传播
.prevent 阻止默认事件
.capture 添加事件侦听器时使用事件捕获模式
.self 只当事件在该元素本身(比如不是子元素)触发时触发回调
.once 事件只触发一次
按键修饰符 : 监听键盘事件时添加按键修饰符
.enter
.tab
.delete (捕获 “删除” 和 “退格” 键)
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
自定义按键修饰符
1 Vue.config.keyCodes.f2 = 113 ;
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" > <h3 > v-on:click</h3 > <button v-on:click ="fOnClick('v-on:click')" > v-on:click</button > <h3 > v-on:keydown(只允许输入数字)</h3 > <input type ="text" v-on:keydown ="fKeyDown($event)" > <h3 > 按键选择器(选择enter)</h3 > <input type ="text" v-on:keydown.enter ="fKeyEnter" > <h3 > v-on:fMouseOver</h3 > <div @mouseover ="divMouseOver" style ="width:300px;height:220px;background-color: red" > <button @mouseover.stop ="btnMouseOver($event)" > btn</button > </div > </div > </body > <script src ="js/vue.js" > </script > <script > new Vue({ el: '#app' , methods: { fOnClick: function (msg) { alert(msg); }, fKeyDown: function (event) { var keycode = event.keyCode; if (keycode < 48 || keycode > 57) { event.preventDefault(); } }, fKeyEnter: function () { alert("您按下了回车。。。" ) }, divMouseOver: function () { alert("鼠标移动到div上了。。。" ); }, btnMouseOver: function (event) { alert("鼠标移动到button上了。。。" ); } } }) </script >
v-model 可以实现表单元素 和Model 中的数据的双向绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <body > <div id ="app" > <h2 > {{msg}}</h2 > <input type ="text" v-model ="msg" style ="width: 500px; height: 35px; font-size: 18px;" > </div > </body > <script src ="js/vue.js" > </script > <script > var vm = new Vue({ el: "#app" , data: { msg: "v-model实现数据的双向绑定" } }); </script >
v-bind 用于绑定属性,可简写成:
绑定属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <body > <div id ="app" > <a v-bind ="{href: 'https://www.baidu.com/s?wd=' + word}" > <input type ="button" v-bind:value ="'点击搜索' + word" > </a > </div > </body > <script src ="js/vue.js" > </script > <script > var vm = new Vue({ el: "#app" , data: { word: "java" } }); </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 <head > <style > .pink { color: pink; } .thin { font-weight: 100; } #size { font-size: 40px; } </style > </head > <body > <div id ="app" > <h2 :class ="'pink'" :id ="'size'" > 引用单个样式</h2 > <h2 :class ="['pink', 'thin']" > 使用数组</h2 > <h2 :class ="flag ? 'pink' : ''" > 使用三元运算符</h2 > <h2 :class ="{'pink': true, 'thin': false}" > 使用对象</h2 > </div > </body > <script src ="js/vue.js" > </script > <script > var vm = new Vue({ el: "#app" , data: { word: "java" , flag: true , style: {color: 'pink' , 'font-weight' : '100' } } }); </script >
使用内联样式
1 2 3 <h2 :style ="{color: 'pink', 'font-weight': '100'}" > 使用内联样式</h2 > <h2 :style ="style" > 引用data中定义的样式</h2 >
v-for和key 用于遍历数组、对象…,
在特殊情况下可能需要使用:key
指定唯一的key
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 <body > <div id ="app" > <span v-for ="item,idx in 5" > {{idx}},{{item}} </span > <br > <br > <span v-for ="item in arr" > {{item}} </span > <br > <br > <span v-for ="prop in person" > {{prop}} </span > <br > <br > <table border ="0px" > <tr > <td > checkbox</td > <td > id</td > <td > name</td > <td > age</td > <td > 操作</td > </tr > <tr v-for ="(person, idx) in persons" :key ="person.id" > <td > <input type ="checkbox" > </td > <td > {{person.id}}</td > <td > {{person.name}}</td > <td > {{person.age}}</td > <td > <input type ="button" v-on:click ="deleteByIdx(idx)" value ="delete" > </td > </tr > <tr > <td > <input type ="checkbox" > </td > <td > <input type ="text" v-model ="id" > </td > <td > <input type ="text" v-model ="name" > </td > <td > <input type ="text" v-model ="age" > </td > <td > <input type ="button" v-on:click ="add" value ="add" > </td > </tr > </table > </div > </body > <script src ="js/vue.js" > </script > <script > var vm = new Vue({ el: '#app' , data: { arr: [1, 2, 3, 4, 5], person: {id:0 , name:'tt' , age:18 }, persons: [ {id:0 , name:'tt' , age:18 }, {id:1 , name:'tt' , age:18 }, ], id: null , name: null , age: null }, methods: { add() { this .persons.push({id: this .id, name: this .name, age: this .age}); }, deleteByIdx(idx) { this .persons.splice(idx, 1 ); } } }) </script >
v-if和v-show
v-if:每次都会删除重新创建元素,频繁操作性能浪费
v-show:只是切换了元素的display属性值,不会删除重建,但有初始消耗(不管显不显示都会创建元素)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <body > <div id ="app" > <button @click ="flag = !flag" > 切换</button > <br > <span v-if ="flag" > hello</span > <span v-show ="flag" > world</span > </div > <script src ="js/vue.js" > </script > <script > new Vue({ el:'#app' , data:{ flag:true } }); </script >
自定义指令 官方文档: https://cn.vuejs.org/v2/guide/custom-directive.html
钩子函数 :
bind: 指令第一次绑定到元素时调用
inserted: 被绑定元素插入到dom中时调用
updated: 当组件(VNode)更新时,会执行updated方法;可能会调用多次。
钩子函数的参数:
el: 被绑定了指令得那个元素,是一个原生得js对象
binding: 一个对象,包含执行相关信息
name
:指令名,不包括 v-
前缀。
value
:指令的绑定值 ,例如:v-my-directive="1 + 1"
中,绑定值为 2
。
expression
:字符串形式的指令表达式
钩子函数使用原则 :
和样式相关的操作,一般都可以在bind中设置
和JS行为相关的操作,最好在inserted中执行
全局指令 :聚焦输入框
1 2 3 4 5 6 7 8 9 Vue.directive("focus" , { inserted: function (el ) { el.focus(); } })
私有指令
1 2 3 4 5 6 7 8 9 10 11 12 var vm = new Vue({ el: "#app" , directives: { "color" : { bind: function (el, binding ) { el.style.color = binding.value; } } } }
函数简写
在很多时候,你可能想在 bind
和 update
时触发相同行为,而不关心其它的钩子
1 2 3 4 5 6 7 8 9 10 Vue.directive("color" , function (el, binding ) { el.style.color = binding.value; }) directives: { color (el, binding) { el.style.color = binding.value; } }
过滤器 Vue.js 允许自定义过滤器,用来作一些常见的文本格式化 。
过滤器可以用在两个地方:mustache 插值和 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 <div id ="app" > {{new Date | dateFormat("yyyy-mm-dd")}} </div > </body > <script src ="js/vue.js" > </script > <script > new Vue({ el: '#app' , filters: { dateFormat(dateStr, pattern) { var date = new Date (dateStr); var year = date.getFullYear(); var month = (date.getMonth() + 1 ).toString().padStart(2 , "0" ); var day = date.getDate().toString().padStart(2 , "0" ); if (pattern && pattern.toLowerCase() === "yyyy-mm-dd" ) return `${year} -${month} -${day} ` ; else return "格式错误!" ; } } }) </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 <div id ="app" > {{new Date | dateFormat("yyyy-mm-dd")}} </div > </body > <script src ="js/vue.js" > </script > <script > Vue.filter('dateFormat' , function (dateStr, pattern) { var date = new Date (dateStr); var year = date.getFullYear(); var month = (date.getMonth() + 1 ).toString().padStart(2 , "0" ); var day = date.getDate().toString().padStart(2 , "0" ); if (pattern && pattern.toLowerCase() === "yyyy-mm-dd" ) return `${year} -${month} -${day} ` ; else return "格式错误!" ; }); new Vue({ el: '#app' }) </script >
注意 :当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用
Vue实例的生命周期 官网文档
组件Component 官网文档: https://cn.vuejs.org/v2/guide/components-registration.html
定义组件 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 <div id ="app" > <my-com1 > </my-com1 > <my-com2 > </my-com2 > </div > <template id ="mytpl" > <h1 > 这是我的第二个component!</h1 > </template > <script > Vue.component("myCom1" , Vue.extend({ template: "<h1 > 这是我的第一个component!</h1 > " })); Vue.component("myCom2" , { template: "#mytpl" }); let vm = new Vue({ el: "#app" , }); </script >
其它拓展
data:是一个函数,并且必须返回一个对象 ,对象中的数据可读可写
props:是一个数组,可以指定组件拥有的属性,其属性值由父组件传递来
methods
directives
filters
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 <div id ="app" > <count id ="count1" > </count > <count id ="count2" > </count > </div > <template id ="tpl" > <div > <button v-on:click ="increment" > ++count</button > <h1 > {{count}}</h1 > </div > </template > <script > Vue.component("count" , { props: ["id" ], template: "#tpl" , data: function () { return { count: 0 } }, methods: { increment() { ++ this .count; } }, }); let vm = new Vue({ el: "#app" , }); </script >
组件切换 VueJS提供了component标签,它的 :is
属性可以用来指定要展示的组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <div id ="app" > <button v-on:click ="componentId='login'" > 登录组件</button > <button v-on:click ="componentId='register'" > 注册组件</button > <component :is ="componentId" > </component > </div > <script src ="js/vue.js" > </script > <script > Vue.component("login" , { template: "<h2 > 这是一个登录组件</h2 > ", }); Vue.component("register" , { template: "<h2 > 这是一个注册组件</h2 > ", }); let vm = new Vue({ el: "#app" , data: {componentId: "login" } }); </script >
父组件向子组件传值 通过属性绑定的方式, 使用props
属性来定义父组件传递过来的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let vm = new Vue({ el: "#app" , data: { msg: "msg!" }, components: { children: { template: "<h2>{{myMsg}}</h2>" , props: ["parentMsg" ], data: function ( ) { return { myMsg : this .parentMsg } } } } });
子组件向父组件传值
通过事件绑定机制,自定义一个事件,并父组件将方法的引用,传递到子组件内部
子组件在内部调用父组件传递过来的方法,并传递数据this.$emit('方法名', 要传递的数据)
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 <div id ="app" > <children v-on:sunclick ="show" > </children > <h2 > {{sunMsg}}</h2 > </div > <script > let children = { template: "<button @click ='myclick' > 获取子组件的Message</button > ", props: ["parentMsg" ], data: function () { return { sunMsg: "子组件Message!" } }, methods: { myclick() { this .$emit("sunclick" , this .sunMsg); } } }; let vm = new Vue({ el: "#app" , data: { sunMsg: "" }, methods: { show(data) { this .sunMsg = data; console .log(data); } }, components: { children } }); </script >
使用ref属性获取组件的引用
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件
$refs
只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs
。
1 2 3 4 5 6 <h2 ref ="h2" > hello</h2 > <comm ref ="comm" > </comm > // vm.$refs.h2.innerText // h2标签的内容 // vm.$refs.comm.msg // comm组件中的msg属性
Vue中的动画 官网文档: https://cn.vuejs.org/v2/guide/transitions.html
transition单元素过度
注意:如果你使用一个没有名字的<transition>
,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my">
,那么 v-enter 会替换为 my-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 <head > <style > .v-enter , .v-leave-to { opacity: 0; transform : translateX (80px ); } .v-enter-active , .v-leave-active { transition : all 0.8s ease ; } </style > </head > <body > <div id ="app" > <button v-on:click ="flag = !flag" > 切换</button > <transition > <h2 v-show ="flag" > 我是一个用于展示的动画切换效果的h2标签!</h2 > </transition > </div > </body > <script src ="js/vue.js" > </script > <script > let vm = new Vue({ el: "#app" , data: { flag: true } }); </script >
结合第三方css库 github: https://github.com/daneden/animate.css
特效展示:https://daneden.github.io/animate.css/
1 2 3 4 5 6 7 8 <button v-on:click ="flag = !flag" > 切换</button > <transition enter-active-class ="bounceIn" leave-active-class ="bounceOut" duration ="2000" > <h2 v-show ="flag" > 我是一个用于展示的动画切换效果的h2标签!</h2 > </transition >
结合钩子函数使用 官方文档: https://cn.vuejs.org/v2/guide/transitions.html#JavaScript-%E9%92%A9%E5%AD%90
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 <head > <style > .ball { width: 20px; height: 20px; border-radius: 50%; background-color: red; } </style > </head > <body > <div id ="app" > <h2 > 模拟小球加入购物车效果</h2 > <button v-on:click ="flag = !flag" > 加入购物车</button > <transition name ="ball" v-on:before-enter ="beforeEnter" v-on:enter ="enter" v-on:after-enter ="afterEnter" > <div v-show ="flag" class ="ball" > </div > </transition > </div > </body > <script src ="js/vue.js" > </script > <script > let vm = new Vue({ el: "#app" , data: { flag: false }, methods: { beforeEnter(el) { el.style.transform = "translate(0px, 0px)" ; }, enter(el, done) { el.offsetWidth; el.style.transform = "translate(300px, 600px)" ; el.style.transition = "all 0.6s ease" ; done(); }, afterEnter(el) { this .flag = false ; } }, }); </script >
列表过度 使用transition-group 标签将li标签包裹起来,并将transition-group标签渲染为ul标签
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 <head > <style > li { width : 100%; line-height: 50px; font-size: 20px; border : 1px dashed #999 ; margin: 10px; padding-left: 25px; } li :hover { background-color: pink; transition : all 0.5s ease ; } .v-enter , .v-leave-to { opacity: 0; transform: translateY(100px); } .v-enter-active , .v-leave-active { transition : all 0.6s ease ; } .v-move { transition : all 0.5s ease;} .v-leave-active { position : absolute;} </style > </head > <body > <div id ="app" > <div style ="padding: 15px 30px;" > <label for ="" > ID: <input type ="text" v-model ="id" > </label > <label for ="" > name: <input type ="text" v-model ="name" > </label > <label for ="" > <input type ="button" value ="添加" v-on:click ="add" > </label > </div > <transition-group tag ="ul" appear > <li v-for ="person, idx of persons" :key ="person.id" @click ="del(idx)" > {{person.id}} ----- {{person.name}}</li > </transition-group > </div > </body > <script src ="js/vue.js" > </script > <script > let vm = new Vue({ el: "#app" , data: { id: null , name: null , persons: [ {id: 0 , name: "tt1" }, {id: 1 , name: "tt2" }, {id: 2 , name: "tt3" }, {id: 3 , name: "tt4" } ] }, methods: { add() { this .persons.push({id: this .id, name: this .name}); this .id = this .name = null ; }, del(idx) { this .persons.splice(idx, 1 ); } } }); </script > </html >
多个组件过渡 多个组件过渡,只需要用transition标签将动态组件标签包裹住,然后设置过度模式即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 v<style > .v-enter , .v-leave-to { opacity: 0; transform: translateY(80px); } .v-enter-active , .v-leave-active { transition : all 0.5s ease ; } </style > <transition mode ="out-in" > <component :is ="componentId" > </component > </transition >
路由Vue-router Vue-router官方文档: https://router.vuejs.org/zh/
后端路由:就是将url地址对应到服务器上的资源
前端路由:在单页面应用程序中,通过改变url中的hash(#号后)来切换页面的方式,称作前端路由
url中hash的特点就是在发送http请求时,不会包含该hash相关内容。
入门案例 定义login、register两个组件,对应两条路线;实现两条路线间的切换。
route-view :用于展示匹配到的组件
router-link :用于链接到指定的路线
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 <style > .myActive { color: red; font-size: 18px; } .v-enter , .v-leave-to { opacity: 0; transform: translateY(80px); } .v-enter-active , .v-leave-active { transition : all 0.5s ease ; } </style > <div id ="app" > <router-link to ="/login" > 登录</router-link > <router-link to ="/register" > 注册</router-link > <transition mode ="out-in" > <router-view > </router-view > </transition > </div > <script src ="js/vue.js" > </script > <script src ="js/vue-router.js" > </script > <script > let login = { template: "<h2 > 这是一个登录组件!</h2 > " } let register = { template: "<h2 > 这是一个注册组件!</h2 > " } const router = new VueRouter({ routes: [ {path: "/" , redirect: "/login" }, {path: "/login" , component: login}, {path: "/register" , component: register} ], linkActiveClass: "myActive" }) let vm = new Vue({ el: "#app" , router: router }); </script >
使用请求参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let login = { template: "<h2>这是一个登录组件! --- 参数:{{$route.query.temp}}</h2>" , } let register = { template: "<h2>这是一个注册组件! --- 参数:{{$route.params.temp}}</h2>" } const router = new VueRouter({ routes: [ {path : "/" , redirect : "/login" }, {path : "/login" , component : login}, {path : "/register/:temp" , component : register} ] })
路由嵌套 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 <div id ="app" > <router-view > </router-view > </div > <template id ="account" > <div > <h1 > 用户模块</h1 > <router-link to ="/account/login" > 登录</router-link > <router-link to ="/account/register" > 注册</router-link > <router-view > </router-view > </div > </template > <script > let account = { template: "#account" } let login = { template: "<h2 > 这是一个登录组件! </h2 > " } let register = { template: "<h2 > 这是一个注册组件! </h2 > " } let vm = new Vue({ el: "#app" , router: new VueRouter({ routes: [ {path: "/" , redirect: "/account" }, { path: "/account" , component: account, children: [ {path: "login" , component: login}, {path: "register" , component: register} ] } ] }) }) </script >
一路由多组件 通过指定router-view的name属性实现上、左、右经典布局
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 <style > html, body { margin: 0px; padding: 0px; font-size: 24px; text-align: center; } .header { background-color: green; height: 80px; } .container { display: flex; } .left { background-color: lightpink; height: 700px; flex: 2; } .main { background-color: pink; height: 700px; flex: 8; } </style > <div id ="app" > <router-view > </router-view > <div class ="container" > <router-view name ="left" > </router-view > <router-view name ="main" > </router-view > </div > </div > <script > let headerBox = { template: `<div class ="header" > 这是头部</div > ` } let leftBox = { template: `<div class ="left" > 这是左边部分</div > ` } let mainBox = { template: `<div class ="main" > 这是主体</div > ` } const router = new VueRouter({ routes: [ { path: "/" , components: { "default" : headerBox, left: leftBox, main: mainBox } } ] }) let vm = new Vue({ el: "#app" , router: router }); </script > </html >
watch监控 watch是一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let vm = new Vue({ el: "#app" , router: router, watch: { "$route.path" : function (newVal, oldVal ) { console .log(newVal + "---------------" + oldVal); if (newVal === "/login" ) { console .log("欢迎进入登录页面!" ); } else if (newVal === "register" ) { console .log("欢迎进入注册页面!" ); } } } });
computed计算属性
计算属性的结果会被缓存下来 ,只有当其依赖的响应式属性变化才会重新计算 。注意,如果某个依赖是非响应式属性 ,则计算属性是不会 被更新的。
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 <div id ="app" > <input type ="text" v-model ="num1" > + <input type ="text" v-model ="num2" > = <input type ="text" v-model ="result" > {{result2}} </div > </body > <script src ="js/vue.js" > </script > <script > let vm = new Vue({ el: '#app' , data: { num1: 0, num2: 0 }, computed: { result: vm => parseInt (vm.num1) + parseInt (vm.num2), result2: function () { return parseInt (this .num1) + parseInt (this .num2); } }, }) </script >
watch、computed和methods区别
computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
methods
方法表示一个具体的操作,主要书写业务逻辑;
watch
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化 ,从而进行某些具体的业务逻辑操作 ;可以看作是computed
和methods
的结合体;
Webpack4 webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具。
可以处理js文件的依赖关系,js的建容问题
官方文档: https://www.webpackjs.com/concepts/
网页中的静态资源 常见的静态资源
JS:.js .jsx .coffee .ts
CSS:.css .less .sass .scss
Images:.jpg .png .gif .bmp .svg
Fonts:.svg .ttf .eot .woff .woff2
模板文件:.ejs .jade .vue
网页中引入的静态资源多了以后有什么问题
网页加载速度慢, 因为 我们要发起很多的二次请求;
要处理错综复杂的依赖关系
如何解决上述两个问题
合并、压缩、精灵图、图片的Base64编码
可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系
打包JS文件 在打包之前,需要安装这两个软件:npm i -g webpack webpack-cli
webpack4默认打包的入口时src/index.js
,出口是dist/main.js
打包方式1
直接命令行:webpack 入口 -o 出口
参数:
--mode production
:生产模式(默认),没有debug代码
--mode development
:开发模式
如:webpack src/main.js -o dist/bundle.js
打包方式2
loader loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)
官网文档: https://www.webpackjs.com/concepts/loaders/
实现打包css文件
安装npm i style-loader css-loader .... --save-dev
修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module .exports = { module : { rules: [ {test : /\.css$/ , use : ["style-loader" , "css-loader" ]}, {test : /\.less$/ , use : ["style-loader" , "css-loader" , "less-loader" ]}, {test : /\.scss$/ , use : ["style-loader" , "css-loader" , "scss-loader" ]} ] } }
在main.js使用import中引入css文件
1 import "./css/index.css"
处理css中的路径问题(图片字体)
运行npm i url-loader file-loader --save-dev
在webpack.config.js
中添加处理url路径的loader模块
1 2 3 4 5 6 7 8 { test: /\.(png|jpg|gif|bmp|jpeg)$/ , use: ["url-loader?limit=10240&name=[hash:8]-[name].[ext]" ] }, {test : /\.(ttf|eot|svg|woff|woff2)$/ , use : "url-loader" , }
使用babel处理高级JS语法
源码: https://github.com/babel/babel-loader
安装相关loader:cnpm install -D babel-loader @babel/core @babel/runtime @babel/plugin-transform-runtime
安装babel转换的语法:cnpm i -D @babel/preset-env
在webpack.config.js
中添加相关loader模块,并排除node_modules
1 2 3 4 5 6 7 8 9 10 11 12 { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: ["@babel/plugin-transform-runtime"] } } }
webpack-dev-server实时打包 这种打包方式会将输出的js文件打包到内存中 ,当我们需要引用这个js文件的时候,直接把它当成在根路径下 即可成功
步骤
安装:npm i -g webpack-dev-server
在webpack.config.js
在package.json
中配置,webpack-dev-server
命令参数
1 2 3 4 5 6 7 { "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" , "dev" : "webpack-dev-server --open --contentBase src" } }
执行命令:npm run dev
,此后我们修改文件后,就会自动打包
webpack-dev-server命令参数说明
--open
:自动打开浏览器
--port
:设置端口
--contentBase
:设置根路径
--hot
:热部署、局部刷新(异步)
也可以在webpack.config.js中指定webpack-dev-server命令参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let path = require ("path" ); let webpack = require ("webpack" );module .exports = { entry: path.resolve(__dirname, "src/main.js" ), output: { path: path.resolve(__dirname, "dist" ), filename: "bundle.js" }, devServer: { open: true , contentBase: "src" , hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ] }
live-server 这是一款带有热加载 功能的小型开发服务器。用它来展示你的HTML / JavaScript / CSS ,但不能用于部署最终的网站。(用于多页面热部署 )
安装:npm install -g live-server
运行:live-server --port=80
html-webpack-plugin 这个插件可以帮我们在内存中生成html页面,并自动帮我们引用打包后的bundle.js文件
安装插件:npm i html-webpack-plugin -D
在webpack.config.js中配置插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let path = require ("path" ); let htmlWebpackPlugin = require ("html-webpack-plugin" );module .exports = { plugins: [ new htmlWebpackPlugin({ template: path.join(__dirname, "src/index.html" ), filename: "index.html" }) ] }
使用vue
安装依赖
cnpm i vue -S
cnpm i vue-loader vue-template-compiler -D
修改配置文件webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const vueLoaderPlugin = require ("vue-loader/lib/plugin" );module .exports = { module : { rules: [ {test : /\.vue/ , use : "vue-loader" } ] }, plugins: [ new vueLoaderPlugin() ], }
创建一个vue单文件组件 , app.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <!-- 组件模板 --> <template> <h2>{{msg}}</h2> </template> <!-- 组件逻辑代码--> <script> export default { data() { return {msg: "hello world"} }, methods: {} // ..... } </script> <!-- 如果不使用scoped属性,则定义的是全局样式 --> <!-- lang属性可以指定语言,如scss,less --> <style scoped> </style>
在入口js文件中引用
1 2 3 4 5 6 7 8 9 import Vue from "vue" import app from "./component/app.vue" let vm = new Vue({ el: "#app" , render: ce => ce(app) });
使用vue-router
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 import "./css/index.css" import Vue from "vue" import VueRouter from "vue-router" import app from "./component/app.vue" import account from "./component/account.vue" import login from "./component/login.vue" import register from "./component/register.vue" Vue.use(VueRouter); const router = new VueRouter({ routes: [ {path : "/" , redirect : "/account" }, { path: "/account" , component: account, children: [ {path : "login" , component : login}, {path : "register" , component : register} ] } ] }) let vm = new Vue({ el: "#app" , render: ce => ce(app), router });
import注意事项
如果是导入当前项目中我们自己的资源,则应使用./
没有./
则会到node_modules目录中去找
node-modules/模块名 –> package.json –> 然后引入main属性指定的文件
vue脚手架 安装@vue/cli
, @vue/cli-init
命令行工具:vue init webpack 项目名
图形工具:vue ui