Vue2.x 简介
什么是Vue
1. 构建用户界面
— 用 vue 往 html 页面中填充数据,非常的方便
2. 框架
— 框架是一套线程的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能
— 学习 vue,就是学习 vue 框架中规定的用法
— vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex
— 只有把上面的内容掌握,才有开发 vue 项目的能力
Vue 的特性
1. 数据驱动视图
在使用了 vue 的页面中,vue 会监听数据的变化,从而自动重新渲染页面的结构
好处:程序员只管把数据维护好,页面结构会被 vue 自动渲染出来
2. 双向数据绑定
在页面中,form 表单负责采集数据,Ajax 负责提交数据
在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源中
页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中
MVVM
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel
在 MVVM 概念中:
Model 表示当前页面渲染时所依赖的数据源
View 表示当页面所渲染的 DOM 结构
ViewModel 表示 vue 的实例,它是 MVVM 的核心
MVVM 的工作原理
当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
vue 的基本使用
vue 的指令与过滤器
1. 指令的概念
指令(Directives)是 vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构
Vue 指令
内容渲染指令
1. v-text 的缺点:会覆盖元素内部原有的内容
2. {{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容
3. v-html:可以把带有标签的字符串渲染成真正的 HTML 内容
属性绑定指令
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中
1. v-bind:为元素的属性动态的绑定值
2. v-on:为元素绑定事件
事件修饰符
.prevent
:阻止默认行为(常用).stop
:阻止事件冒泡(常用).captrue
:以捕获模式触发当前的事件处理函数.once
:简写为~
绑定的事件只触发一次.self
:只有在 event.target
是当前元素自身时触发事件处理函数.passive
:简写为&
,主要用于滚动监听.captrue
:简写为!
,用于监听冒泡,若有captrue
则先冒泡带有关键字的事件,然后再从里向外执行.captrue.once
或.once.captrue
:简写为~!
v-model指令
作用范围
- input 输入框
- textarea
- select
修饰符 - number:自动将用户输入的值转为数值类型
- trim:自动过滤用户输入的首尾空白字符
- lazy:在 “change(选中)” 时而非 “input(输入)” 时更新
条件渲染指令
- v-if:每次动态创建或移除元素,实现元素的显示和隐藏
- v-show:动态为元素添加或移除
display: none
,来实现元素的显示和隐藏
频繁切换元素的显示状态,使用 v-show 性能更好
如果只需要切换一次元素状态,使用 v-if 性能更好
过滤器
过滤器必须定义在 filters 对象里面,必须要有 return 返回值
过滤器函数接收的第一个形参是前面管道符的值
<div id="app">
<p>msg 的值是:{{msg | capi}}</p>
</div>
// 过滤器函数
filters: {
// 过滤器函数形参中的 val,永远都是‘管道符’前面的那个值
capi(val) {
// 字符串有 charAt 方法,这个方法接受索引值,表示从字符串中把索引对应的字符串,获取出来
const first = val.charAt(0).toUpperCase()
// 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
const other = val.slice(1);
// 强调:过滤器中一定要有一个返回值
return first + other
}
}
侦听器
- 方法格式侦听器
缺点1:无法在刚进入页面的时候,自动触发
缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器 ```javascript
watch: { // 侦听器本质上是一个函数,要监视哪个数据的变化,就把该数据名作为方法名即可 // newVal 是变化之后的值,oldVal 是变化之前的值 username(newVal, oldVal) { if(newVal === ‘’) return // 1. 调用 jQuery 中的 Ajax 发起请求,判断 newVal 是否被占用 $.get(‘https://www.escook.cn/api/finduser/‘ + newVal, (result) => { console.log(result); }) } }
2. 对象格式的侦听器<br />好处1:可以通过 immediate 选项,让侦听器自动触发<br />好处2:可以通过 deep 选项,让侦听器深度监听每个属性的变化
```javascript
<div id="app">
<input type="text" v-model="username">
</div>
watch: {
// 定义对象格式的侦听器
username: {
// 侦听器的处理函数
handler(newVal, oldVal) {
console.log(newVal, oldVal);
},
// 默认值为 false
// 控制侦听器自动触发一次
immediate: true
}
}
计算属性
特点:
- 定义的时候要被定义为 方法
- 在使用计算属性的时候,当普通的属性使用即可
好处: - 实现了代码的复用
只要计算属性中依赖的数据源变化了,则计算属性会重新计算求值
<div id="app">
<div>
<span>R:</span>
<input type="text" v-model.number="r">
</div>
<div>
<span>G:</span>
<input type="text" v-model.number="g">
</div>
<div>
<span>B:</span>
<input type="text" v-model.number="b">
</div>
<hr>
<!-- 专门用户呈现颜色的 div 盒子 -->
<div class="box" :style="{ backgroundColor: rgb }">
{{ rgb }}
</div>
<button @click="show">按钮</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(this.rgb);
}
},
// 所有的计算属性都要定义到 computed 节点之下
// 计算属性在定义的时候,要定义成“方法格式”
computed: {
// rgb 作为一个计算属性,被定义成了方法格式
// 返回一个生成好的 rgb() 字符串
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
})
</script>
axios
axios 是一个专注于网络请求的库!
vue-cli
什么是 vue-cli
vue-cli 是 Vue.js 开发的标准工具
它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程
安装、查看与使用 Vue-cli
安装vue-cli: npm install -g @vue/cli
查看vue-cli版本:vue -V
运行 vue 项目:npm run serve
快速生成工程化的 Vue 项目:vue create 项目的名称
vue 项目中 src 目录的构成
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表
components 文件夹:程序员封装的、可复用的组件
main.js 项目的入口文件,整个项目的运行,要先执行 main.js
App.vue 是整个项目的根组件
Vue 组件的三个组成部分
template:组件的模板结构
script:组件的Javascript行为
style:组件的样式
注意点:组件模板中必须包含有且只有一个根元素,要启用 less 语法必须在 style 标签内加上 lang=”less” 属性才行
注册全局组件
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件
// 导入需要全局注册的组件
import Count from ‘@/component/Count.vue’
// 参数1:字符串格式,表示组件的“注册名称”
// 参数2:需要被全局组测的那个组件
Vue.component(‘MyCount’, Count)
Props
props 是自定义属性,允许使用者通过自定义属性,为当前组件指定初始值
props 中的数据可以直接在模板结构中被使用
props 是只读的,不要直接修改 props 的值,否则终端会报错
在封装通用组件的时候,合理的使用 props 可以极大的提高组件的复用性
Vue 组件
生命周期
生命周期是指一个组件从 创建 -> 运行 -> 销毁 的整个阶段,强调的是一个时间段
组件传值 —- day04\vue-components
props 中的值是不可被修改的
父传子:在父组件上实例化并调用子组件,然后在子组件上添加 props 数组,在父组件里指定 props 中值的内容
// 父组件
<template>
<div class="father-countainer">
<div class="box">
<son-one :msg="msg" :user="userinfo"></son-one>
</div>
</div>
</template>
<script>
import SonOne from '@/components/Son-one.vue'
export default {
data() {
return {
msg: 'hello vue.js',
userinfo: { name: 'zs', age: 20 },
// 定义 countFromSon 来接收子组件传递过来的数据
countFromSon: 0
}
},
components: {
SonOne
}
}
</script>
// 子组件
<template>
<div class="son-one">
<h2>Son-one 子组件</h2>
<p>父组件传过来的 msg 值是:{{ msg }}</p>
<p>父组件传过来的 user 值是:{{ user }}</p>
</div>
</template>
<script>
export default {
props: ['msg', 'user']
}
</script>
子传父:在子组件上使用 this.$emit(‘事件类型名称’, 要传的值) ,在父组件中使用 子组件中的传出的自定义事件,然后通过自定义事件得到子组件传过来的值
// 父组件
<template>
<div class="father-countainer">
<h1>App 组件 ---- {{ countFromSon }}</h1>
<hr>
<div class="box">
<son-two @numchange="getNewCount"></son-two>
</div>
</div>
</template>
<script>
import SonTwo from '@/components/Son-two.vue'
export default {
data() {
return {
// 定义 countFromSon 来接收子组件传递过来的数据
countFromSon: 0
}
},
components: {
SonTwo
},
methods: {
getNewCount(val) {
this.countFromSon = val;
}
}
}
</script>
// 子组件
<template>
<div class="son-two">
<h2>Son-two 子组件</h2>
<p>{{ count }}</p>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
// 子组件自己的数据,把 count 值传给父组件
count: 0
}
},
methods: {
add() {
this.count ++;
// 修改数据时,通过 $emit() 触发自定义事件
this.$emit('numchange', this.count)
}
}
}
</script>
兄弟传值:定义一个 eventBus.js 文件,在 eventBus.js 文件中向外共享 Vue 的实例对象,在数据发送方和数据接收方引入 eventBus.js 文件,并绑定对应事件即可
- 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
- 在数据发送方,调用 bus.$emit(‘事件名称’, 要发送的数据)方法触发自定义事件
- 在数据接收方,调用 bus.$on(‘事件名称’, 事件处理函数)方法注册一个自定义事件
```vue
// 兄弟组件1
Son-one 子组件
// 兄弟组件2
Son-two 子组件
// 显示接收到的数据/值{{msgFromLeft}}
<a name="2ca39c98"></a>
#### ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用<br />每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的 DOM 元素或组件的引用。默认情况下,组件的 $refs 指向一个空对象<br />ref 可以定义在组件上,获取组件中的所有信息,以及方法等
<a name="b3e79ab2"></a>
### 动态组件 -------》day05/demo-1-component
<a name="component"></a>
#### component标签
component 标签是 vue 内置的<br />作用:组件的占位符
```javascript
<component is=""></componenet> 中的 is 属性的值表示需要渲染的组件的名称
keep-alive标签
作用:防止组件之间相互切换时重复创建和销毁,把内部的组件进行缓存,而不是销毁组件
默认值:默认缓存标签中所包含的全部组件
<keep-alive>
<component is=""></componenet>
</keep-alive>
- activated()事件:组件被激活了要执行的方法
- deactivated()事件:组件被缓存了要执行的方法
- include属性:指定需要缓存的组件名称
- exclude属性:指定不需要缓存的组件名称
注意点:include 和 exclude 不能同时间使用
插槽(slot)
插槽是 vue 为组件的封装者提供的能力。允许开发者再封装组件时,把不确定的、希望用户指定的部分定义为插槽
v-slot 指令 —- v-slot: ===
- v-slot 可以简写为 #
- v-slot: === #
- v-s lot: 不能直接用在元素身上,只能用在 template 标签中,而 template 标签只是起到包裹作用,并不会作为标签元素渲染在页面中
- 如果要把内容填充到指定名称的插槽中,需要使用 v-slot: 这个指令,并且 v-slot: 的冒号后面需要跟上插槽的名字
- Vue 官方规定:每一个 slot 插槽,都要有一个 name 属性的名称,如果省略了 slot 的 name 属性,则有一个默认名称叫做 default
作用域插槽
- 在封装组件时,为预留的 提供属性对应的值,这种用法叫做 “作用域插槽”
作用域插槽可以进行解构赋值,在 template 标签中使用插槽,并对其进行解构赋值
// 作用域插槽 <div class="content-box"> <slot name="content" msg="hello vue.js"></slot> </div> // 渲染插槽 <template #content="obj"> <p>{{ obj.msg }}</p> // 渲染的结果为 "hello vue.js" </template>
自定义指令
- vue 中的自定义指令分为两类:私有自定义插槽,全局自定义指令
私有自定义指令
- 在每个 vue 组件中,可以在 directives 节点下声明 私有自定义变量,如下所示:
directives: { color: { // 为绑定到的 HTML 元素设置红色的文字 bind(el, binding) { // 形参中的 el 时绑定了此指令的、原生的 DOM 对象 el.style.color = binding.value; } } }
- 自定义指令可以在标签中的 v-color=”” 接收绑定指令的值
- 在 directives 属性中使用定义一个 binding 参数,使用 binding.value 获取指令绑定的值
- binding 参数是官方建议定义的,当然也可以是任何值
- 自定义属性中 有一个 expression 属性,可以获取到 v-color 等号后面的值
- 自定义指令中有一个定义在 directives 中的自定义指令里的 update 方法,在 DOM 更新的时候触发
directives: { color: { // 为绑定到的 HTML 元素设置红色的文字 bind(el, binding) { // 在第一次绑定时执行 // 形参中的 el 时绑定了此指令的、原生的 DOM 对象 el.style.color = binding.value; }, // 在 DOM 更新的时候,会触发 update 函数 update(el, binding) { // 更新时触发执行 el.style.color = binding.value; } } }
- 当自定义指令中的 bind 和 update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
directives: { // 在 bind 和 update 时,会触发相同的业务逻辑 color(el, binding) { el.style.color = binding.value; } }
全局自定义指令
- 全局共享的指令需要通过 Vue.directive() 进行声明:
// 参数1:字符串,表示全局自定义指令的名字 // 参数2:对象,用来接收指令的参数值 // 定义在 main.js 文件内 Vue.directive('color', (el, binding) { el.style.color = binding.value; })
- 全局声明的自定义指令和过滤器需要定义在 main.js 文件中
Axios补充
- 可以将 Axios 挂载到项目中的 main.js 文件中去,这样做的好处就是不用在组件中去引入 axios,直接在组件中使用 this.$http 调用即可 ```javascript // main.js import axios from ‘axios’ // 引入 axios 模块 // 全局配置 axios 的请求根路径 axios.defaults.baseURL = ‘请求根路径’ // 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用 Vue.prototype.$http = axios
// .vue 的组件 async 方法名() { // get() 内的东西是根组件后面要请求的路径名称 const { data: res } = await this.$http.get(‘/api/get’); console.log(res); }
- 缺点:无法实现 API 接口的复用 <a name="YwWVi"></a> # Vue 静态资源引用方法注意点 <a name="zp0wu"></a> ## 图片 ```javascript // 通过 require(utrl) 的形式将静态图片资源导入加载到页面中 userlist: [ { username: '川', headerImg: require('./assets/img/1.jpg') }, { username: '海', headerImg: require('./assets/img/2.jpg') }, { username: '清', headerImg: require('./assets/img/3.jpg') }, { username: '辰', headerImg: require('./assets/img/4.jpg') } ]
渲染函数 render
export default { render(h) { return h('div', 'Hello,world!!!') } }
// 原 html模板 <template> <ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li> </ul> <p v-else>No items found.</p> </template> // 使用 render 函数重绘 <script> export default { props: ['items'], // item 数组需要在父组件中进行赋值 render: function (createElement) { if (this.items.length) { return createElement('ul', this.items.map(function (item) { return createElement('li', item.name) })) } else { return createElement('p', 'No items found.') } } } </script>
props: ['value'], render: function (createElement) { var self = this return createElement('input', { domProps: { value: self.value }, on: { input: function (event) { self.$emit('input', event.target.value) } } }) }
v-if
和v-for
相对于v-model
,可以更好的控制交互细节// 安装预设 npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props // 在 babel.config.js 中设置 module.exports = { presets: ['@vue/babel-preset-jsx'], }