Vue 基础
Vue 简介
- Vue 是一套用于构建用户界面的渐进式框架。
- 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。
- Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
# react 是 facebook开发的
# vue 是我们国人开发
vue起步
- 1. 引包
- 2. 启动 new Vue(options);
插值表达式 一定要 双括号 -> {{ msg }}
Vue指令
v-text和v-html
- {{ }} 和 v-text 的作用一样 都是插入值,直接渲染,innerText
- v-html 既能插入值,又能插入标签 innerhtml
v-bind绑定
v-bind: 相当于 ' : ' 简写
v-on事件处理 监听dom
v-on 简写 ' @ '
v-on
事件修饰符
.once
<!-- 点击事件将只会触发一次 --> <a v-on:click.once="doThis"></a>
为什么在 HTML 中监听事件?
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。
但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。
实际上,使用 v-on 有几个好处:
- 1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
- 2. 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
- 3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。
v-for列表渲染
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
这个类似 Vue 1.x 的 track-by="$index"。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute:
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>
建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
v-model双向数据绑定
<p>{{obj.a}} {{obj.b}}</p> <input type="text" v-model="obj.a" > data: obj:{ num : 0, a:1, b:"nima"
}
Vue中表单输入绑定应用
修饰符
.lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:
<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg">
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
<input v-model.number="age" type="number">
这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
<input v-model.trim="msg">
侦听器watch的用法
<div id="app">
<h3>{{abc[0].name}}</h3> <button @click="abc[0].name='addpapa'">changer</button>
</div>
<script src="./vue.js"></script> <script > var v = new Vue({ el : '#app', data:{ abc:[ {id:1, name:"123"}, {id:2, name:"bb"}, {id:3, name:"cc"}, ], } // 基本的数据类型可以使用watch直接监听,复杂数据类型object array 要深度监视 watch:{ // key是属于data 对象的属性名,value:监听后的行为,n_v:新值,o_v:旧值 'mmm':function (n_v,o_v) { console.log(n_v, o_v) }, // 深度监视 object | array 对象。数组 'abc':{ deep:'true', //这是字符串 handler:function (new_v) { console.log(new_v[0].name); } } }
计算属性之computed getter
计算属性之computed的setter方法
<div id="app"> {{content}} </div> <script src="./vue.js"></script> <script> // 全局的 过滤器 Vue.filter("m_reverse",(val) => { console.log(this) return val.split("").reverse().join("") }) new Vue({ el:'#app', data:{ msg:"", }, methods:{ input_mgs:function (event) { const {value} = event.target; this.content = value; }, click_mgs:function () { console.log(this.content) } }, }) </script>
过滤器 filters
为数据添加新东西
<div id="app"> {{f | my_f("$")}} <br> {{ff | m_reverse}} <br> {{content}} <input type="text" v-model="content" @input="input_mgs"> <button @click="click_mgs" >changer</button> </div> <script src="./vue.js"></script> <script> // 全局的 过滤器 Vue.filter("m_reverse",(val) => { console.log(this) return val.split("").reverse().join("") }) new Vue({ el:'#app', data:{ msg:"", f:123, ff:"aadddd", }, methods:{ input_mgs:function (event) { const {value} = event.target; this.content = value; }, click_mgs:function () { console.log(this.content) } }, // 局部的 过滤器 filters:{ my_f:function (f,a) { return a+f } }, computed:{ content:{ set:function (new_v) { this.msg = new_v console.log(new_v) }, get:function () { return this.msg } }, }, }) </script>
音乐播放器
练手项目:音乐播放器
<head> <meta charset="UTF-8"> <title>音乐播放器</title> <style> *{ padding: 0; margin: 0; } ul { list-style: none; } ul li{ margin: 30px 30px; padding: 20px 10px; border-radius: 20px; } ul li.active{ background-color: lavender; } </style> </head> <body> <div id="app"> <audio :src="get_src" controls autoplay @ended="handleend()" ></audio> <ul> <li :class="{active:index === dataindex}" v-for='(item, index) in musicData' :key="item.id" @click="handleClick(index)"> <h3>{{item.id}} 歌名: {{item.name}} </h3> <p>作者: {{item.author}}</p> </li> </ul> <button @click="handlenext()">下一首</button> </div> <script src="./vue.js" ></script> <script > const musicData = [ { id:0, name :'信仰', author:'hc', src:'./static/信仰.mp3' }, { id:1, name :'春娇与志明', author:'hc', src:'./static/春娇与志明.m4a' }, { id:2, name :'暗示分离', author:'辉哥', src:'./static/暗示分离.mp3' }, { id:3, name :'突然的自我', author:'hc', src:'./static/突然的自我.mp3' }, { id:4, name :'风吹麦浪', author:'hc', src:'./static/风吹麦浪.m4a' } ] new Vue({ // 绑定 标签 el: "#app", data:{ musicData, // src:"./static/信仰.mp3", dataindex: 0, }, // vue 的计算属性 computed:{ get_src(){ return this.musicData[this.dataindex].src }, }, methods:{ handleClick(index){ // this.src = src this.dataindex = index }, handleend(){ this.handlenext(); }, handlenext(){ this.dataindex++ if (this.dataindex ===this.musicData.length){ this.dataindex = 0 } // this.src = this.musicData[this.dataindex].src } } }) </script>
组件
局部组件的创建和使用
- App 组件 html+css+js
- 1. 创建组件
- 2. 挂载子组件
- 建,挂,用
- 注意: 在组件中这个data 必须是一个函数,返回一个对象
<div id="App"> <!-- // 3. 使用子组件--> <App></App> </div> <script src="../vue.js" ></script> <script> // 以首字母开头大写 , 第一个参数是组件名,第二个是 模板 Vue.component("Vheader",{ template: ` <div>我是导航栏 </div> ` }) Vue.component('Vaside',{ template:` <div> 我是侧边栏 </div> ` }) // 需要用到的局部变量,代码需要写在 创建好的子组件前面 const Vbutton={ template:` <button>局部按钮</button> ` } // 局部组价 const Vcontent={ data(){ return{} }, template:` <div> <div>我是内容栏</div> <Vbutton/> <Vbutton/> </div> `, components:{ Vbutton, }, } // App 组件 html+css+js // 1. 创建组件 // 2. 挂载子组件 // 建,挂,用 // 注意: 在组件中这个data 必须是一个函数,返回一个对象 const App ={ data(){ return { msg: "我是App组件" } }, <!-- 一定要有一个闭合的标签 --> template:` <div> <div> <Vheader> </Vheader> <div> <Vaside /> <Vcontent/> </div> </div> <h3>{{msg}}</h3> <button @click='handleClick'> 按钮</button> </div> `, methods:{ handleClick(){ this.msg='学习局部组件'; } }, components: { Vcontent, } } var a = new Vue({ el:'#App', data:{}, components: { // 挂载子组件 App, }, }) </script>
全局组件的创建和使用
vue 提供的监听触发事件 把输出的值,通过事件抛出给父组件
this.$emit('inputHandler',val)
$on 绑定事件
$emit 触发事件
provide 和 inject
<div id="App"> <!-- // 3. 使用子组件--> <App> </App> </div>
<script src="../vue.js" ></script> <script> // provide // inject // 父组件 provide 来提供变量, 然后再子组件中通过inject 来注入变量,无论组件嵌套多深 // 做了个中间传递者的功能 中央事件总线 var c = new Vue // 这里的this 是这个A子组件本身的this Vue.component('AA',{ template:` <div>AA 使用provide 和 inject -> : {{msg}}</div> `, inject:["msg"] }) Vue.component('A',{ // data(){ // return{ // msg:"provide + inject -> A -> AA -> A " // } // }, template: ` <div> <AA></AA> <button @click="countNumber">购物车+1</button> </div> `, methods:{ countNumber(){ // $emit 触发事件 c.$emit('add',1) } } }) // 这里的this 是这个B子组件本身的this Vue.component('B',{ data(){ return { number:0 } }, template:` <div> {{number}} </div> `, // 组件创建 created(){ // $on 绑定事件 // $emit 触发事件 c.$on('add',(n)=>{ this.number+=n; }) } }) const App ={ data(){ return {} }, // 需要绑定一个属性 ( : 属性, @ 事件 ) template:` <div> <A></A> <B></B> </div> `, } new Vue({ el:'#App', data:{ }, provide(){ return { msg:"provide + inject -> App-> A -> AA " } }, components: { // 挂载子组件 App, }, }) </script>
匿名插槽
<slot></slot>
具名插槽
<slot name="aa"></slot>
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的
生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated 激活
- deactivated 停用
- 需要配合 keep-alive 保存在内存中
- beforeDestroy
- destroyed
<div id="App"> <!-- // 3. 使用子组件--> <App></App> </div>
<script src="../vue.js" ></script> <script> // beforeCreate // created // beforeMount // mounted // beforeUpdate // updated // activated 激活 // deactivated 停用 // 需要配合 keep-alive 保存在内存中 // beforeDestroy // destroyed Vue.component('test',{ data() { return { msg:"你辉哥哥哥", isRed:false }; }, methods:{ handleClick(){ this.msg = "Appyourpapa"; this.isRed = !this.isRed ; } }, template: ` <div> <button @click="handleClick">改变</button> <h3 :class='{active:isRed}'>{{msg}}</h3> </div> `, beforeCreate(){ console.log("组件创建之前 -> beforeCreate",this.$data ) }, created(){ // 非常重要的事情,在此时发送ajxa 请求后端数据 console.log("组件创建完成 -> create", this.$data) }, beforeMount(){ console.log("组件挂载之前 -> beforeMount", document.getElementById("app")) }, mounted(){ console.log("组件挂载完成 -> Mount", document.getElementById("app")) }, beforeUpdate(){ console.log("组件更新之前的DOM -> beforeUpdate", ) }, updated(){ console.log("组件更新完成的DOM -> updated", ) }, beforeDestroy(){ console.log("组件销毁之前的DOM -> beforeDestroy") }, destroyed(){ console.log("组件销毁完成的DOM -> destroyed") }, activated(){ console.log("组件被激活了 -> activated") }, deactivated(){ console.log("组件被停用了 -> deactivated") }, }) const App ={ data(){ return { isShow:true } }, methods: { clickHandler(){ this.isShow = !this.isShow; }, }, // 一定要有一个闭合的标签 // 需要绑定一个属性 ( : 属性, @ 事件 ) template:` <div> <keep-alive> <test v-if="isShow"></test> </keep-alive> <button @click="clickHandler">销毁和创建</button> </div> ` } new Vue({ el:'#App', data:{ }, provide(){ return { msg:"provide + inject " } }, components: { // 挂载子组件 App, }, }) </script>
异步组件加载
<script src="../vue.js" ></script> <script type='module'> const App ={ data(){ return { isShow:false } }, methods: { clickHandler(){ this.isShow = !this.isShow; }, }, components:{ test:()=>import('./test.js') }, template:` <div> <test v-if="isShow"></test> <button @click="clickHandler">异步加载</button> </div> `, } new Vue({ el:'#App', data:{ }, provide(){ return { msg:"provide + inject " } }, components: { App, }, }) </script>
refs的使用
访问子组件实例或子元素
有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref
这个 attribute 为子组件赋予一个 ID 引用。例如:
<base-input ref="usernameInput"></base-input>
现在在你已经定义了这个 ref
的组件里,你可以使用:
this.$refs.usernameInput
来访问这个` <base-input>
`实例,以便不时之需。比如程序化地从一个父级组件聚焦这个输入框。在刚才那个例子中,该 <base-input>
组件也可以使用一个类似的 ref
提供对内部这个指定元素的访问,例如:
<input ref="input">
甚至可以通过其父级组件定义方法:
methods: {
// 用来从父级组件聚焦输入框
focus: function () {
this.$refs.input.focus()
}
}
允许父级组件通过下面的代码聚焦 `<base-input>
`里的输入框:
this.$refs.usernameInput.focus()
当 ref
和 v-for
一起使用的时候,你得到的 ref 将会是一个包含了对应数据源的这些子组件的数组。
nextTick的使用 和 应用
<div id="App"> <!-- // 3. 使用子组件--> <App></App> </div>
<script src="../vue.js" ></script> <script >
// 在页面上拉取一个接口,这个接口返回一些数据,这些数据是这个页面的一个浮层组件要依赖的, // 然后我在接口一返回数据就展示了这个浮层组件,展示的同时 // 上报一些数据给后台(这些数据是父组件从接口拿的) // 这个时候,神奇的事情发生了,虽然拿到了数据,但是浮层展现的时候, // 这些数据还未更新到组件去,上报失败 const Pop = { data(){ return{ isShow:false, } }, props:{ name : { type: String, default:'', }, }, template:` <div v-if="isShow"> {{name}} </div> `, methods:{ show(){ // 弹窗组件展示 this.isShow = true; console.log(this.name); } } } const App=({ data(){ return{ name :"" } }, created(){ // 模拟异步请求 setTimeout(()=>{ // 更新数据 this.name = "huige" this.$nextTick(()=>{ this.$refs.pop.show(); }) // this.$refs.pop.show(); },1000); }, components:{ Pop }, template: ` <pop ref="pop" :name="name"></pop> ` }) var vm = new Vue({ el:'#App', components: { App } }) </script>
对象变更检测注意事项
<div id="App"> <h3> {{user.name}}, {{user.age}}, {{user.phone}} <button @click="handlerAdd">添加响应式属性</button> </h3> </div>
<script src="../vue.js" ></script> <script > // Vue 不能检测对象属性的添加和删除 new Vue({ el:'#App', data:{ user:{} }, methods:{ handlerAdd(){ this.user.age = 20 // 添加响应式属性, 只有 添加或者删除 有效 // vue 的方法需要都要加 $ //1. Vue.$set(object, key, value) // this.$set(this.user,"age",20) // 2. Object.assign 方法 this.user = Object.assign({}, this.user,{ age:200, phone:110, }) } }, created(){ setTimeout(()=>{ this.user={name:"我是你爸爸"} },2000) }, }) </script>
mixin混入技术
// 一个公共的组件 const mymixin={ data(){ return{ msg:"asd" } },created(){ this.hello(); }, methods:{ hello(){ console.log("hello mixin") } } } new Vue({ el:'#app', data(){ return { msg1:"huige" } }, created(){ console.log("aaa") }, // mixin 来分发Vue 组件中 的可复用 功能! mixins:[mymixin], })
mixin混入技术应用
<div id="app"> </div>
<script src="../vue.js" ></script> <script>
// 模态框 和 提示框 逻辑程序的复用 // 全局的mixin 每个创建的组件都会被调用。 // 用法 Vue.mixin({}) const allData={ data() { return { isShow:false } }, methods:{ allData(){ this.isShow= !this.isShow } } } const MoDuleData= { template:` <div v-if='isShow'> <h1> 模态框 </h1> </div> `, mixins:[allData] } const ToolData = { template:` <div v-if='isShow'> <h4> 提示框 </h4> </div> `, mixins:[allData] } new Vue({ el:'#app', data:{}, components:{ MoDuleData, ToolData, }, template:` <div> <button @click="handlemoduel">模态框</button> <button @click="handletool">提示框</button> <MoDuleData ref='modulea'></MoDuleData> <ToolData ref='toola'></ToolData> </div> `, methods: { handlemoduel(){ this.$refs.modulea.allData(); }, handletool(){ this.$refs.toola.allData(); }, } }) </script>