Vue.js是一个渐进式、基于MVVM设计模式的纯前端JavaScript框架。它可以与其他技术混用,适用于以数据操作为主的项目,无需依赖后端技术。下面是关于Vue.js的一些重要知识点整理:
• 只保存可能变化的节点的简化版DOM树 • new Vue()时,vue对象通过扫描真实DOM树,只将可能变化的元素,保存到虚拟DOM树上。
• vue会快速遍历虚拟DOM树,找到受影响的元素,调用已经封装好的DOM函数,只更新页面中受影响的元素。不受影响的元素,不会改变
• 1. 小,仅保存可能变化的元素 • 2. 快, 遍历快,查找快 • 3. 自动,已经封装了DOM操作,自动修改页面。避免了大量重复的DOM操作
• 如何: <any v-bind:属性=”数据变量” • 强调: 不用写{{}} • 简写: v-bind可省略, 仅: :属性=”数据变量”
• 如何: <元素 v-show="条件"> • 原理: 每次扫描时,如果条件为true,就保持当前元素原样显示。如果条件为false,则自动为当前元素加上display:none,隐藏该元素 • vs v-if: 通过添加/删除元素来控制显示隐藏。
• 如何: <元素1 v-if="条件1"> <元素2 v-else> • 两个元素之间不能插入其它元素,必须紧挨着写。 • 和js程序一样,v-else后不需要写任何条件 • 原理: 每次扫描时判断条件的值,如果条件为true,就显示元素1,删除元素2;如果条件为false,就显示元素2,删除元素1 • 强调: 不是用display:none隐藏,而是彻底删除不显示的元素
• <any v-if="条件"></any><any v-else-if="条件"></any><any v-else></any> • 多个元素之间不能插入其它元素,必须紧挨着写。 • 原理: 每次扫描时判断条件的值,哪个元素条件为true,就显示哪个元素,并删除其它元素;如果之前所有条件都不满足,就显示最后一个元素,删除之前所有元素
• 如何 • <元素 @事件名="处理函数(实参值,...)"> • new Vue({ el:"#app", data: { ... }, methods:{ 处理函数(形参){ //this->当前new Vue()对象 //可用this.变量名方式访问data中的变量,因为methods中的方法,也是被打散后直接隶属于new Vue()的。其实和data中的变量打散后是平级的。都直接隶属于new Vue()对象 } }}) • 其实,如果不传参,则@事件名="函数"后不需要加() • 传参 • <元素 @事件名="处理函数(实参值,...)"> • methods:{ 处理函数(形参值){ //this->当前new Vue()对象 //可用this.变量名方式访问data中的变量,因为methods中的方法,也是被打散后直接隶属于new Vue()的。其实和data中的变量打散后是平级的。都直接隶属于new Vue()对象 } } • 获得事件对象 • 只获得事件对象,不需要传其它自定义参数时 • <元素 @事件名="处理函数"> • 一定不要加(),因为加()是调用函数且不传参数的意思 • 不加()是绑定事件处理函数的意思 • 只有不加()时,vue才会像DOM一样自动将事件对象以处理函数第一个参数方式,传入处理函数 • methods:{ event ↓ 处理函数(e){ e自动获得事件对象。获得的事件对象,和DOM中的事件对象完全一样 } } • 既需要获得事件对象,又需要传入自定义参数 • <元素 @事件名="处理函数($event, 其它实参值,...)"> • 因为加了(),就无法自动传入事件对象了 • 所以,必须手动传入事件对象和其它实参值 • $event是vue将DOM中事件对象重新封装过的一个代表事件对象的关键词 • vue中所有$开头的关键词,都不能改名 • methods:{ $event 其它实参 ↓ ↓ 处理函数(e, 其它形参,....){ e自动获得事件对象。获得的事件对象,和DOM中的事件对象完全一样 } }
• 如何: <any v-for="(value, i) of 数组/对象/字符串" :key="i"> • of可改为in,但是in和of效果是一样的。只是为了满足不同人的习惯而已 • 强调: v-for要写在要重复生成的元素上,而不是父元素上。 • 原理 • of可自动遍历数组/对象,并取出数组/对象中每个元素的值(value)和下标(i)保存到左边的变量中 • v-for,每遍历数组或对象中一个成员,就会创建一个当前HTML元素的副本。 • v-for里of前写的变量,在当前元素内部可用于绑定语法。v-for反复生成元素过程中,就会将绑定语法中的变量替换为变量值,固定在元素上 • :key="i" • 不加:key="i" • 反复生成的元素是无差别的,如果将来数组或对象中某一个成员值发生了改变,就无法精确找到并区分要更改的是哪一个元素,只能将这组元素全部重新生成一遍——效率低 • 加:key="i" • 等于对每个元素加上一个不重复的标识i • 如果将来数组或对象中某一个成员值发生了改变,即可根据key属性的值精确找到要更改的一个HTML元素,只更改受影响的一个HTML元素即可,不用将这组HTML元素全部重新生成一遍——效率高 • 坑 • 当数组中保存的是原始类型的值时 • 在程序中修改数组中某个元素值时,不能使用[下标]方式访问 • 因为此时下标方式是不受监控的 • 结果: 即使修改成功,也不会自动更新 • 应该用.splice(i,1,"新值") 代替 • 删除现在i位置的值,替换为一个新值 • 所有数组函数,都是受监控的 • 结果: 只要一修改, 界面上自动变化 • 但是,如果数组中保存的是引用类型的对象,则可以用[下标]修改 • v-for还会数数 • <元素 v-for="i of 整数"> • 结果: of会从1开始,循环到这个整数,循环了几次,就将当前HTML元素重复创建几次。of前的i会依次获得 1 2 3 4 5 可用于绑定在元素的内容中。比如分页按钮
• 问题: • 因为{{}}绑定html片段内容时,会保持html片段原样显示,而不是翻译为页面元素。所以{{}}不能用于绑定HTML片段内容 • 解决 • v-html绑定html片段时,会将HTML片段交给浏览器去解析为页面元素
• v-cloak:(哈利波特的隐身斗篷) 强调: 不是clock!!! • 问题: 首次记载页面时,如果加载稍微延迟,用户就可能看到{{}}语法! • 什么是v-cloak: 让当前元素默认隐藏,直至内容准备就绪,才自动显示出来 • 如何: 2步: 必须配合css使用 • <要隐藏的元素 v-cloak> • 强调: VUE官方本身,没有提供v-cloak的隐藏样式,所以,必须我们自己写。 • 在CSS中必须添加: [v-cloak] { display: none; } • 用属性选择器找到所有带有v-cloak的元素,让其暂时隐藏 • 当new Vue()加载完成,就会找到页面中所有v-cloak属性,并移除他们。这样,暂时隐藏的元素,就都显示出来了。 • VUE只负责自动移除v-cloak属性,所以v-cloak不能改名 • v-text:使用模型数据替换当前元素的innerText • <元素 v-text="变量或js表达式"></元素> • 将原本用{{}}绑定的内容,放在指令中绑定。效果是一样的 • 但是,因为指令属于属性,即使暂时没有加载完,用户也不会看到属性部分的内容。所以,起到了避免用户看到双花括号的作用
• 底层原理:只在首次加载时,一次性将模型数据显示在当前元素 。不会将当前元素加入到虚拟DOM树中 • 优化: 减少被监视的元素个数,可以优化页面绑定的效率。
• 何时: 如果元素内容中,有{{}}原文,但不想作为绑定语法解析时,可用v-pre,保留{{}}为原文,不再编译。
• v-model:value会被自动翻译为oninput="function(){...}"事件绑定代码,并在事件处理函数中封装修改data中变量的代码 • 只要文本框内容被修改,就触发DOM的oninput事件,自动执行修改data中变量的代码
• 用不是直接修改select元素的文本,而是通过选择option来改变select的value
• 单向绑定时 • 将Model中的变量值赋值给select的value属性.然后, select元素会拿获得value属性值去和每个option的value值做比较.哪个option的value等于select的value,就选中哪个option • 修改时 • <select v-model:value="xxx",会被自动翻译为: <select onchange=" vue对象.变量=当前select元素的value属性值 "
• 备选项的value都是固定不变的, 所以肯定绑定的不是value属性 • 选中与不选中radio改变的其实是它的checked属性
• 单向绑定时 • v-model会拿checked属性绑定的变量值和当前radio的value属性进行比较. 如果绑定的变量的值等于当前radio的value,就选中该radio.否则,如果绑定的变量值,不等于当前radio的value,就不选中该radio
• 不需要和value做比较,直接用checked属性绑定到一个bool值变量即可
• 单向绑定时 • 绑定的变量值返回true,就选中,返回false,就不选中 • 修改时 • 直接将checkbox当前的选中状态checked属性值更新回魔心变量上
• 监事函数的函数名,必需是要监事的变量名
• <元素 style="left:100px; top:50px">
• 如果希望仅修改其中一个css属性值,就很不方便
• Vue绑定语法会将对象翻译为style属性规定的字符串格式 • <元素 style="left:100px; top:50px">
• <元素 :style="{ left: 变量1, top: 变量2 }" • data:{ 变量1:"100px", 变量2:"50px"} • <元素 style="left:100px; top:50px">
• <元素 style="不变的css样式" :style="变量" • 结果: • vue会先绑定:style,翻译为字符窜,然后再和不带:的style拼接为一个style • 所以,今后不需要动态绑定的css内联样式属性,就可放在不带:的style中. 只有那些需要动态改变的css属性,才放在带: 的style中
• <元素 class="class1 class2 ...">
• 如果希望仅修改其中一个class,就很不方便
• Vue绑定语法会将对象翻译为class字符串,但是只有那些值为true的class,才会存在于最后的class中.值为false的class,表示不启用
• <元素 :class="{ class1: 条件1, class2: 条件2 }"
• <元素 class="不变的class" :class="变量" • 结果: • vue会先绑定:class,翻译为字符窜,然后再和不带:的class拼接为一个class作用在元素上 • 所以,今后不需要动态绑定的class,就可放在不带:的class中. 只有那些需要动态改变的class,才放在带: 的class中
• Vue.js生态系统(Ecosystem): Vue.js、Axios、Vue-Router、Vuex、MintUI、ElementUI、Weex....
• axios.get(“url”,{ params: { //get方式下, 随url发送的参数 }}).then(res=>{ ... res.data … })
• axios.post(“url”, “变量=值&变量=值&…”).then(res=>{ ... res.data … }) • 坑!!!: get和post方法传参时,配置属性名不一样: • Get请求传参,用的是params: { } 对象 Post请求传参,必须用字符串! • 解决: • 引入qs模块 • 专门负责将对象转为查询字符串语法 • <script src="js/qs.min.js" • 使用 • axios.post(“url”, Qs.stringify({ 参数: 值, 参数: 值, ... })).then() • 结果: • stringify会将对象转化为queryString语法 • "参数=值&参数=值&..."
• 无论是get/post方式请求,获得的响应都是一个全新的对象。传统的响应数据,包含在该对象的res.data属性中 • 所以,res.data,才能获得之前的响应结果
• <div id="app"> ...<div>new Vue({ el:"#app", data: { ... }})
• 子主题 2
• Vue.component( '组件名' , { template: `HTML片段`, data(){ return {....} }, methods:{ }, computed: { }, watch: { }} ) • Vue.component('组件名', { • //组件名推荐写法: xz-counter 用横线分割多个单词,不推荐使用驼峰命名。因为组件名其实就是今后的HTML标签名。HTML标签是不区分大小的。单靠大小写不能唯一标识组件名 • template: ` • 不用el,是因为组件并不是一开始就在界面上的,是无法查找到的 • 组件每使用一次,就会创建一次HTML片段的副本。最初定义的这一次HTML片段就称为之后组件的模板 • //强调: 组件模板中,必须只能有一个父级根元素 • //如果不加唯一父元素,报错: Component template should contain exactly one root element. • /*data:{ count: 1 }*/ • //报错: The "data" option should be a function that returns a per-instance value in component definitions. • 因为每个组件要求必须有一个专属的data对象副本,而不是多个组件公用一个data对象 • data: function(){ return { }} • 每使用一次组件,会自动调用data()函数,为本次组件副本创建一个data对象副本。来保证每个组件都有一个专属的data对象副本,互不影响
• <div id="app"> <组件名></组件名></div>
• //也是一个组件,而且是整个页面的根组件
• //只有根组件,才能使用el属性来绑定根元素 • //其它自定义子组件中,都必须使用template属性代替el: • //其余以下属性,子组件也可使用
• 1. 先定义子组件对象 • var xzChild1={ template: '', data(){return {}}} • 子组件命名,强烈建议用驼峰命名 • //内容和Vue.component中的内容是一样的 • 2. 将子组件对象添加到父组件中 • Vue.component('父组件', {template: ' … … <子组件名/>… … ', //包含局部组件元素的父组件模板HTMLcomponents: { //专门用于包含子组件的定义xzChild1, //vue会自动将驼峰翻译为-分割。<xz-child1> ...}})
• 2种: • 父->子 • props down • 2步 • 先在父组件中给子组件的自定义属性绑定一个父组件的变量 • <template id="父"> ... ... <child :自定义属性名="父组件的变量"></child> • 结果: 将父组件中一个变量的值保存在子组件的一个自定义属性上 • 子组件 • var child={ ... ... props:["自定义属性名"]} • 结果: 子组件对象中,可取出父组件放在子组件自定义属性上的变量值 • props中的变量用法和data中变量用法完全一样,只不过值的来源不同 • 如果父给子传递的是原始类型的值,其实传递的是原值的一个副本。所以在子组件中修改变量的值,不影响父组件。 • 如果父给子传递的是一个引用类型的对象或数组,其实传递的是对象的地址。在子组件中修改变量,会影响父组件 • 子->父 • event up • 2步 • 父组件 • <template id="父"> ... .... <child @自定义事件="父的处理函数"Vue.component("父",{ ... methods:{ 父的处理函数(参数){ 参数得到子组件触发事件($emit)时,传递过来的数据 } }}) • 子组件 • js中,任何时候: this.$emit("父定义的事件名",this.数据)
• 数据总线机制 • 3步 • 创建全局空的Vue实例:bus • var bus=new Vue() • 接收者 • 在bus上绑定自定义事件,并提供处理函数 • Vue.component("...",{ ... ... mounted(){ //this->当前组件对象 bus.$on("自定义事件",(参数)=>{ //必须用箭头函数: 内外公用this! //要求this->当前组件对象 //参数: 将会得到将来兄弟传来的值 }) }}) • 发送者 • 可在任意时候,触发bus上的别人自定义的事件: bus.$emit("别人自定义的事件",this.数据)
• 多个.html文件
• 只有一个完整的.html文件,其余"页面",其实都是组件
• 删除整棵DOM树,重新下载新的.html文件,重建新的DOM树
• 重新加载一个页面组件,不需要重建整棵DOM树,而是局部替换原DOM树中指定元素位置即可
• 即使有可重用的资源(css或js),每个页面也必须重新请求一次
• 只在首次加载时,就请求一次。之后切换页面,不需要重新请求。
• 不可能实现
• 轻松实现
• 路由字典是包含相对路径和页面组件间对应关系的数组 • var router=new VueRouter({ routes:[ {path:"/", component: 默认首页的页面组件 }, {path:"/相对路径", component: 页面组件 }, ... ... {path:"*", component:notFound} ] })
• router对象监控着地址栏中的路径 • 只要地址栏中路径变化,就拿新的路径在路由字典中查找是否有匹配的路由 • 如果有匹配的路由,就找到该路由对应的页面组件对象 • 用页面组件对象代替唯一完整的.html文件中<router-view>预留的位置
• {path:"/相对路径/:参数名", component:xxx, props:true}
• props:["参数名"]
• /相对路径/参数值
• 参数值会自动传给props中的参数名属性,在页面组件中,可用this.参数名方式,访问参数值!
• this.$route.params.lid
• npm config get registry
• npm config set registry https://registry.npm.taobao.org
• 会下蛋的老母鸡
• 下蛋
• 1. 启动一个简易的开发服务器(类似于live server) • 2. 编译脚手架中代码为传统的HTML CSS和js代码
• npm cache clean -f
• npm i -save axios
• 将axios放入Vue的原型对象中,今后在vue中任何位置都可this.axios.get()方式访问! • 在main.js中new Vue()之前 • import axios from "axios" • 设置服务器端基础地址 • axios.defaults.baseURL="http://localhost:5050"; • 将设置好的axios对象,放入Vue的原型对象中 • Vue.prototype.axios=axios; • 结果: • 在所有组件对象内,都可用途this.axios.get()发送请求
• index.html<html> <head> <link> <script> </head> <body> <div id="app"> <router-view> </div> <script> new Vue({ ... ... }) </body>
• public文件夹/ imgs/当前项目用到的所有图片 css/ 当前项目公用的第三方css,比如bootstrap.min.css js/ 当前项目公用的第三方js,比如: jquery.min.js, bootstrap.min.js index.html 唯一完整的html文件,只包含<div id="app">,不包含<router-view>和new Vue()src/文件夹下 App.vue <div id="app"> <my-header></my-header> <router-view> 可能还包含所有网页公用的样式和公用的组件,比如页头. main.js import axios from "axios" import MyHeader from "./components/MyHeader" axios.defaults.baseURL="http://localhost:5050"; Vue.prototype.axios=axios; Vue.component("my-header",MyHeader) new Vue({...})总结: App.vue+main.js,放入index.html后,才相当于咱们做的完整的index.html
• index.jsvar index={ template:`html片段`, data(){ return {}}, methods:{ }, ... ...}details.jsnotFound.js
• src/文件夹下 views/文件夹下 放所有页面组件 有几个页面,就要有几个组件 每个组件都是一个.vue文件。每个vue文件由三部分组成: <template> 当前组件的HTML片段,要求只能有一个统一的父元素包裹。 </tempalte> <script> export default { 没有template, 组件结构: data(){return {}}, methods:{...}, watch:{ ... }, computed:{ ... }, components:{ } } </script> <style scoped> 仅当前组件使用的css </style>每个.vue文件,最后都会被编译为传统的html,css和js,才能被浏览器认识
• 在index.html顶部用script src="index.js"引入页面组件对象然后router.jsvar routes=[ ... ];var router=new VueRouter({routes})
• src/文件夹下 router/文件夹 index.js 先引入页面组件 import 页面组件 from "../views/页面组件.vue" new VueRouter({[ 路由字典 ]})
• 手动加入index.html中的new Vue()中
• src/main.js 自动添加 new Vue({ router ,..})
• my-header.jsVue.component()
• src/文件夹下 components/ my-header.vue 每个组件的组成三部分,和页面组件的三部分是完全一样的
• src/文件夹下 assets/文件夹 css/ 自己编写的所有页面公用的css js/ 自己编写的所有页面公用的js
• <script>export default { data(){ ... }, methods:{ ... }, ... ...}</script>
• components/MyHeader.js • <script>export default { ... ...}</script>
• 1). 先将组件对象引入到main.js中: • 2). 让组件对象变成全局组件 • main.js中 • import MyHeader from "./components/MyHeader.vue" • Vue.component("my-header",MyHeader) • MyHeader才会成为全局组件
• export { getIndex(){ ... ... }}
• 先引入 • import {getIndex} from "../assets/js/apis/index.js" • 再getIndex().then(result=>{}) • 强调: • 必须使用箭头函数,为了保证回调函数内的this和vue中的this保持一致
• 组件创建之前自动调用 —— $el: undefined, data: undefined
• 组件创建完成自动调用 —— $el: undefined, data: { … } ——已可以获取或操作模型数据——可ajax请求
• 组件挂载到DOM树之前调用 —— $el: undefined, data: { … }
• 组件挂载到DOM树之后调用 —— $el: DOM, data: { … } ——可ajax请求数据,也可操作页面元素
• 组件中模型数据发生改变需要更新DOM之前调用
• 组件中模型数据发生改变需要更新DOM之后调用
• 组件被从DOM上销毁之前调用
• 组件被从DOM上销毁之后调用
• 其中/**/注释是webpack专用数值,其中的chunkname:"名称"作为将来webpack打包时的分文件名,所以,这个注释中的名字要尽量和当前路由或页面组件名保持一致
• npm i -save @babel/plugin-syntax-dynamic-import
• 其中prefetch意思是,一旦网络空闲,就自动预先下载独立.js文件
• 悄悄的浪费手机流量!
• 新建vue.config.js文件 • 是vue/cli 3.0版本的新的webpack配置文件 • 在vue.config.js中 • module.exports={ chainWebpack:config=>{ config.plugins.delete("prefetch") }} • 结果: 移除将来编译后的<link>或<script>标签上的prefetch属性,不再自动预先下载不需要的js或css
• 在需要缓存的路由上添加meta:{keepAlive:true} • { path: '/', name: 'home', component: Home, meta:{ keepAlive:true } } • 其中, • meta不能改名!因为meta是路由对象专门定义,用来保存自定义的属性值的配置项 • 但是keepAlive是自定义的属性,可以改名
• 如果当前路由需要缓存($route.meta.keepAlive==true),就放在keep-alive包裹的一个router-view中 • 如果当前路由不需要缓存($route.meta.keepAlive==false),就放在keep-alive外的一个router-view上 • <keep-alive> <router-view v-if="$route.meta.keepAlive"/></keep-alive><router-view v-if="!$route.meta.keepAlive"></router-view>
• 假如有一个商品列表页面,可以根据关键词,查询商品列表 • 如果从首页跳转过来,说明用户新输入了查询条件,需要更新查询结果 • 如果从详情页跳转过来,说明用户从商品列表页面跳出去的,现在又返回商品列表页面,那么应该保留之前的搜索结果。
• Home.vue中 • beforeRouteLeave(to,from,next){ console.log(`路由离开home...`); //如果从首页跳到products if(to.name=="products"){ to.meta.keepAlive=false; } next(); }
• Products.vue中 • beforeRouteLeave(to, from , next){ console.log("离开商品列表页面"); if(to.name=="details"){ from.meta.keepAlive=true; } next(); }
• Details.vue • beforeRouteLeave(to, from, next){ console.log(`路由离开details...`); if(to.name=="products"){ to.meta.keepAlive=true; } next(); },
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。