Vue核心 绑定样式 条件渲染
1.绑定样式
class样式
写法: :class="xxx" ,xxx 可以是字符串、数组、对象
:style="[a,b]" 其中a、b是样式对象
:style="{fontSize: xxx}" 其中 xxx 是动态值
字符串写法适用于:类名不确定,要动态获取
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
<style>
.basic {width: 300px;height: 50px;border: 1px solid black;}
.happy {border: 3px solid red;background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);}
.sad {border: 4px dashed rgb(2, 197, 2);background-color: skyblue;}
.normal {background-color: #bfa;}
.baiye1 {background-color: yellowgreen;}
.baiye2 {font-size: 20px;text-shadow: 2px 2px 10px red;}
.baiye3 {border-radius: 20px;}
</style>
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br/><br/>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br/><br/>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div><br/><br/>
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br/><br/>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
2. 条件渲染
v-if
写法 跟 if else 语法类似
v-if="表达式"
v-else-if="表达式"
v-else
适用于:切换频率较低的场景,因为不展示的 DOM 元素直接被移除
注意: v-if 可以和 v-else-if v-else 一起使用,但要求结构不能被打断
v-show
写法: v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉 display: none
备注:使用 v-if 的时,元素可能无法获取到,而使用 v-show 一定可以获取到
template 标签不影响结构,页面 html 中不会有此标签,但只能配合 v-if ,不能配合 v-show
3.列表渲染
v-for 指令
用于展示列表数据
语法: <li v-for="(item, index) of items" :key="index">
,这里 key 可以是 index ,更好的是遍历对象的唯一标识
可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
<div id="root">
<!-- 遍历数组 -->
<h3>人员列表(遍历数组)</h3>
<ul>
<li v-for="(p,index) of persons" :key="index">{{ p.name }}-{{ p.age }}</li>
</ul>
<!-- 遍历对象 -->
<h3>汽车信息(遍历对象)</h3>
<ul>
<li v-for="(value,k) of car" :key="k">{{ k }}-{{ value }}</li>
</ul>
<!-- 遍历字符串 -->
<h3>测试遍历字符串(用得少)</h3>
<ul>
<li v-for="(char,index) of str" :key="index">{{ char }}-{{ index }}</li>
</ul>
<!-- 遍历指定次数 -->
<h3>测试遍历指定次数(用得少)</h3>
<ul>
<li v-for="(number,index) of 5" :key="index">{{ index }}-{{ number }}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: '张三', age: 18 },
{ id: '002', name: '李四', age: 19 },
{ id: '003', name: '王五', age: 20 }
],
car: {
name: '奥迪A8',
price: '70万',
color: '黑色'
},
str: 'hello'
}
})
</script>
面试题: react vue 中的 key 有什么作用?( key 的内部原理)
- 虚拟DOM 中 key 的作用: key 是 虚拟DOM 中对象的标识,当数据发生变化时, Vue 会根据新数据生成新的 虚拟DOM ,随后 Vue 进行新 虚拟DOM 与旧 虚拟DOM 的差异比较,比较规则如下
- 对比规则
a. 旧 虚拟DOM 中找到了与新 虚拟DOM 相同的 key
ⅰ. 若 虚拟DOM 中内容没变, 直接使用之前的 真实DOM
ⅱ. 若 虚拟DOM 中内容变了, 则生成新的 真实DOM ,随后替换掉页面中之前的 真实DOM
b. 旧 虚拟DOM 中未找到与新 虚拟DOM 相同的 key
创建新的 真实DOM ,随后渲染到到页面 - 用 index 作为 key 可能会引发的问题
a. 若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的 真实DOM 更新 ==> 界面效果没问题,但效率低
b. 若结构中还包含输入类的 DOM :会产生错误 DOM 更新 ==> 界面有问题 - 开发中如何选择 key ?
a. 最好使用每条数据的唯一标识作为 key ,比如 id、手机号、身份证号、学号等唯一值
b. 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用 index 作为 key 是没有问题的
Vue核心 计算属性
- 定义:要用的属性不存在,需要通过已有属性计算得来
- 原理:底层借助了 Objcet.defineproperty() 方法提供的 getter 和 setter
- get 函数什么时候执行?
a. 初次读取时会执行一次
b. 当依赖的数据发生改变时会被再次调用 - 优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便
- 备注
a. 计算属性最终会出现在 vm 上,直接读取使用即可
b. 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变
c. 如果计算属性确定不考虑修改,可以使用计算属性的简写形式
watch 监视属性
深度监听
注意:
- Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以
使用 watch 时根据监视数据的具体结构,决定是否采用深度监视
watch: { //正常写法 // isHot: { // //handler什么时候调用?当ishot发生改变时,调用 // //immediate:true//初始化时让handler调用一下 // // deep:true 深度监视 // handler(newValue, oldValue) { // console.log('ishot被修改了') // } // } //简写 // isHot(newValue, oldValue) { // console.log('isHot被修改了', newValue, oldValue); // } }
正常写法
vm.$watch('isHot', { //immediate:true//初始化时让handler调用一下 // // deep:true 深度监视 // handler(newValue, oldValue) { // console.log('ishot被修改了') // } })
计算属性 VS 监听属性
computed 和 watch 之间的区别
computed 能完成的功能, watch 都可以完成
watch 能完成的功能, computed 不一定能完成,例如 watch 可以进行异步操作
两个重要的小原则
所有被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数)
最好写成箭头函数,这样 this 的指向才是 vm 或 组件实例对象
小知识
多数被vue管理的函数用普通函数
多数不被vue管理的函数最好用箭头函数
v-if 多和template使用
模拟一个数据监测
let data = {
name: '小花',
address: '武汉'
}
// 创建一个监视的实例对象,用于检测data中属性的变化
const obs = new Observer(data)
console.log(obs)
//声明一个vm实例对象
let vm = {}
vm._data = data = obs
function Observer(obj) {
const keys = Object.keys(obj)
keys.forEach(k => {
Object.defineProperty(this, k, {
get() {
return obj[k]
},
set(val) {
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM...我要开始忙了`);
obj[k] = val
}
})
})
}
Vue监测数据改变的原理
1.vue会监视data中所有层次的数据
2.如何检测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据.
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
3.如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用元素怒哼对应的方法对数组进行更新
(2).重新解析模板,进行页面渲染.
4.在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push() pop() shift() unshift() reverse() splice() sort()
(2)Vue.set()或 vm.$set()
特别注意:Vue.set() 和vm.$set() 不能给vm或者vm的根数据对象添加属性!!!
收集表单数据
收集表单数据:
若:<input type="text">,则v-model手机的时value值,用户输入的就是value的值
若:<input type="radio">,则v-model收集的是value,且要给标签配置value值
若:<input type="checkbox">
1.没有配置input的value属性,那么手机的就是checked(勾选or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么手机的就是checked(勾选 or 不勾选,是布尔值)
(2)v-model的初始值是数组,那么手机的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点在收集数据
number:输入字符串转为有效的数组
trim:输入首尾空格过滤
插值表达式
vue有一个最基本的功能: 数据渲染。 将数据(变量,或者利用ajax从后端获取到的数据)展示到页面上。
这里它不会直接操作dom,而是有自己的语法
在vue中{{ }}语法,叫做:插值表达式,大白话就是输出{{ }}中的表达式的值的语法。
把{{ }} 理解为一个占位符(一个坑), {{ msg }} 就是把msg显示在这个占位符中(把msg插到坑里面边去)
语法
{{ }} 可以:
● 写data数据字段名称
● 对data数据字段进行表达式运算
○ 拼接
○ 算术运算
○ 三元运算
○ ...
{{}}不能:
● js语句:声明变量,分支,循环
● 访问在vue实例中的data之外定义的自定义的变量
过滤器
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用一些简单逻辑操作)
语法:
1.注册过滤器:Vue.filter(name,callback)或new Vue(filters:{})
2.使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性='xxx| 过滤器名'
备注:
1.过滤器也可以接收额外参数、多个过滤器可以串联
2.并没有改变原来的数据,是产生新的对应的数据
内置指令
之前学过的指令:
v-bind 单向绑定解析表达式,可简写为:
v-model 双向数据绑定
v-for 遍历数组 / 对象 / 字符串
v-on 绑定事件监听,可简写为 @
v-show 条件渲染 (动态控制节点是否展示)
v-if 条件渲染(动态控制节点是否存存在)
v-else-if 条件渲染(动态控制节点是否存存在)
v-else 条件渲染(动态控制节点是否存存在)
v-text指令:
1.作用:向其所在的节点中渲染文本内容
2.与插值语法的区别:v-text会替换掉节点中的内容.{{xxx}}则不会
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会
(2).v-html可以识别html解构
3.严重注意:v-html有安全性问题!!!
(1).在网站上动态渲染任意HTML时非常危险的,容易导致xss攻击
(2).一定要在可信的内容上使用v-html,永不要在用户提交的内容
v-once指令:
1.v-once所在节点在初次渲染后,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能.
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题.
v-pre指令:
1.跳过其所在节点的编译过程
2.可以利用它跳过:没有使用指令语法、没有使用插值语法的节点、会加快编译.
自定义指令总结:
一.定义语法:
(1)局部指令
new Vue({ new Vue({
directives:{指令名:配置对象} 或 directives{指令名:回调函数}
}) })
(2)全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
二.配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用
(2).inserted:指令所在元素被插入页面时调用
(3).update:指令所在模板结构被重新解析时调用
三.备注:
1.指令定义时不加v-,但是用是要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名.
自定义指令例子:备用图片处理
export const imgerror = {
// el 指令所在的元素
// binding 指令的相关信息对象, binding.value 指令的值
inserted(el, binding) {
// console.log(el, bindings)
el.onerror = function() {
// console.log('图片加载失败了'), 设置备用图片地址
el.src = binding.value
}
}
}
引出声明周期
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子
2、是什么:Vue在关键时刻帮我们调一些特殊名称的函数
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据实际需求编写的。
4。生命周期哈桑农户中的this指向是vm或组件实例对象。
常用生命周期钩子:
1.mounted:发送ajax请求、绑定自定义事件、订阅消息登[初始化操作]
2.beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息
2.销毁后自定义事件会失效,但原生DOM事件依然有效
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会触发更新流程了.
Vue中使用组件的三大步骤:
一.定义组件(创建组件)
二.注册组件
三.使用组件(写组件标签)
组件:局部功能的代码和资源的集合
一.如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options
几乎一致,但区别如下:
1.el不要写,为什么?-------最终所有的组件都要经过一个vm的管理,由于cm中的vm决定服务那个容器
2.data必须写成函数,为什么?------避免组件被复用时,数据存在引用关系.
备注:使用template可以配置组件结构
二.如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
三.编写组件标签
<school></school>
关于组件名
1.一个单词组成
第一种写法(首字母小写):school
第二种写法(首字母大写):School
2.多个单词组成
第一种写法(kebab-case 命名):my-school
第二种写法(CamelCase 命名):MySchool(需要 Vue 脚手架支持)
备注
组件名尽可能回避 HTML 中已有的元素名称,例如:h2、H2都不行
可以使用 name 配置项指定组件在开发者工具中呈现的名字
3.关于组件标签
1.第一种写法: <school></school>
2.第二种写法: <school/> (需要 Vue 脚手架支持)
备注:不使用脚手架时, <school/> 会导致后续组件不能渲染
一个简写方式: const school = Vue.extend(options) 可简写为 const school = options ,因为父组件 components 引入的时候会自动创建
关于 VueComponent
a. school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,而是 Vue.extend() 生成的
b. 我们只需要写 <school/> 或
c. 每次调用 Vue.extend ,返回的都是一个全新的 VueComponent ,即不同组件是不同的对象
d. 关于 this 指向
ⅰ. 组件配置中 data 函数、 methods 中的函数、 watch 中的函数、 computed 中的函数 它们的 this 均是 VueComponent实例对象
ⅱ. new Vue(options) 配置中: data 函数、 methods 中的函数、 watch 中的函数、 computed 中的函数 它们的 this 均是 Vue实例对象
e. VueComponent 的实例对象,以后简称 vc (组件实例对象) Vue的实例对象 ,以后简称 vm
一个重要的内置关系
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
2.为什么要有这个关系:让组件实例对象Vc可以访问到上Vue的属性、方法
Vue单文件组件
.vue是什么?
.vue文件是单文件组件
- 后缀名是
.vue
- webpack会使用额外的loader来处理它
- 一个
.vue
文件就是一个组件(页面) - 整个项目(页面)就是由多个组件构成的
基本组成
由三个部分组成:template + script + style
- template : 决定模块。类似于.html
- script: 代码逻辑。类似于.js
- style: 样式
<template>
<div class="box">
我是html模板
</div>
</template>
<script>
// 我是js逻辑
export default {
data() {
return {
// 定义变量,数据
}
}
}
</script>
<style>
/* 我是css样式 */
.box {
color:red
}
</style>
组件进阶-动态组件
格式
<component :is="comName"></component>
子组件
<template>
<div>
<h2>UserName</h2>
<p>用户名:<input /> </p>
<p>密码:<textarea /> </p>
</div>
</template>
<script>
export default {
}
</script>
父组件
<template>
<div>
<button @click="comName = 'UserName'">账号密码填写</button>
<button @click="comName = 'UserInfo'">个人信息填写</button>
<p>下面显示注册组件:</p>
<div style="border: 1px solid red">
<!-- vue内置的组件component, 可以动态显示组件 -->
<component :is="comName"></component>
</div>
</div>
</template>
<script>
import UserName from "./UserName";
import UserInfo from "./UserInfo";
export default {
data() {
return {
comName: "UserName",
};
},
components: {
UserName,
UserInfo,
},
};
</script>
注意
is只能是动态属性=》:is="组件注册后的标签名字符串或data变量"
不能直接拿注册标签名赋值使用
Vue内置的组件component,可以动态显示组件.
动态绑定is,他的值就是组件的名字(compoinents中的定义)来控制把哪个组件装进来
组件进阶-keep-alive组件
组件切换会导致组件被频繁销毁和重新创建, 大多数情况下是有自己的意义的,但也可能会导致不必要的性能损耗
keep-alive
使用Vue内置的keep-alive组件, 可以让包裹的组件保存在内存中不被销毁
格式
演示2: 使用keep-alive内置的vue组件, 让动态组件缓存而不是销毁
补充生命周期:
● activated - 激活
● deactivated - 失去激活状态
<keep-alive>
<!-- vue内置的组件component, 可以动态显示组件 -->
<component :is="comName"></component>
</keep-alive>
组件进阶-keep-alive组件-指定缓存
语法
- include="组件名1,组件名2..."
:include="['组件名1', '组件名2']"
<keep-alive include="name1,name2"> <!-- vue内置的组件component, 可以动态显示组件 --> <component :is="comName"></component> </keep-alive>
注意:
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)Vue CLI 初始化脚手架
说明
- Vue脚手架 是 Vue 官方提供的标准化开发工具(开发平台)
- 最新的版本是 4.x
- 文档 Vue CLI https://cli.vuejs.org/zh/
具体步骤
- 如果下载缓慢请配置 npm 淘宝镜像 npm config set registry http://registry.npm.taobao.org
- 全局安装 @vue/cli npm install -g @vue/cli
- 切换到创建项目的目录,使用命令创建项目 vue create xxx
- 选择使用 vue 的版本
- 启动项目 npm run serve
- 打包项目 npm run build
暂停项目 Ctrl+C
Vue脚手架 隐藏了所有 webpack 相关的配置,若想查看具体的 webpack 配置,请执行 vue inspect > output.js
. 脚手架文件结构
文件目录 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue │ │── App.vue: 汇总所有组件 │ └── main.js: 入口文件 ├── .gitignore: git版本管制忽略的配置 ├── babel.config.js: babel的配置文件 ├── package.json: 应用包配置文件 ├── README.md: 应用描述文件 └── package-lock.json: 包版本控制文件
关于不同版本的函数
- vue.js 与 vue.runtime.xxx.js 的区别
a. vue.js 是完整版的 Vue ,包含:核心功能+模板解析器
b. vue.runtime.xxx.js 是运行版的 Vue ,只包含核心功能,没有模板解析器
esm 就是 ES6 module - 因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容
vue.config.js 配置文件
vue inspect > output.js 可以查看到Vue脚手架的默认配置
使用 vue.config.js 可以对脚手架进行个性化定制,和 package.json 同级目录,
详见 配置参考 | Vue CLI https://cli.vuejs.org/zh/config/#vue-config-js
module.exports = {
pages: {
index: {
entry: 'src/index/main.js' // 入口
}
},
lineOnSave: false // 关闭语法检查
}
Vue CLI ref props mixin plugin scoped
1.ref属性
ref
被用来给元素或子组件注册引用信息(id的替代者)
应用在 html
标签上获取的是真实 DOM元素
,应用在组件标签上获取的是组件实例对象 vc
使用方式
a. 打标识: <h1 ref="xxx"></h1>
或 <School ref="xxx"></School>
b. 获取: this.$refs.xxx
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
<School ref="sch"/>
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components:{ School },
data() {
return {
msg:'欢迎学习Vue!'
}
},
methods: {
showDOM(){
console.log(this.$refs.title) // 真实DOM元素
console.log(this.$refs.btn) // 真实DOM元素
console.log(this.$refs.sch) // School组件的实例对象(vc)
}
},
}
</script>
2. props 配置项
props 让组件接收外部传过来的数据
传递数据
接收数据
第一种方式(只接收) props:['name', 'age']
第二种方式(限制类型) props:{name:String, age:Number}
第三种方式(限制类型、限制必要性、指定默认值)
props: {
name: {
type: String, // 类型
required: true,// 必要性
default: 'cess'// 默认值
}
}
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
备注:props是只读的, Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props 的内容到 data 中,然后去修改 data 中的数据
3.mixin 混入
- 功能:可以把多个组件共用的配置提取成一个混入对象
使用方式
a. 定义混入const mixin = { data() {....}, methods: {....} .... }
b. 使用混入
ⅰ. 全局混入 Vue.mixin(xxx)
ⅱ. 局部混入 mixins:['xxx']
备注组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先
var mixin = { data: function () { return { message: 'hello', foo: 'abc' } } } new Vue({ mixins: [mixin], data () { return { message: 'goodbye', bar: 'def' } }, created () { console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" } } })
- 同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用
var mixin = {
created () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
4.plugin 插件
- 功能:用于增强 Vue
- 本质:包含 install 方法的一个对象, install 的第一个参数是 Vue ,第二个以后的参数是插件使用者传递的数据
- 定义插件(见下 src/plugin.js)
使用插件: Vue.use()
export default { install(Vue,x,y,z){ console.log(x,y,z) //全局过滤器 Vue.filter('mySlice', function(value){return value.slice(0,4)}) //定义全局指令 Vue.directive('fbind',{ //指令与元素成功绑定时(一上来) bind(element,binding){element.value = binding.value}, //指令所在元素被插入页面时 inserted(element,binding){element.focus()}, //指令所在的模板被重新解析时 update(element,binding){element.value = binding.value} }) //定义混入 Vue.mixin({ data() {return {x:100,y:200}}, }) //给Vue原型上添加一个方法(vm和vc就都能用了) Vue.prototype.hello = ()=>{alert('你好啊')} }
5.scoped 样式
- 作用:让样式在局部生效,防止冲突
写法:
<style scoped>
Vue 中的 webpack 并没有安装最新版,导致有些插件也不能默认安装最新版,如 npm i less-loader@7,而不是最新版<template> <div class="demo"> <h2 class="title">学校名称:{{ name }}</h2> <h2>学校地址:{{ address }}</h2> </div> </template> <script> export default { name:'School', data() { return { name:'湖北', address:'襄阳', } } } </script> <style scoped> .demo{ background-color: skyblue; } </style>
Vue CLI Todo-List案例
组件化编码流程
- 拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突
- 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
一个组件在用:放在组件自身即可
一些组件在用:放在他们共同的父组件上(状态提升) - 实现交互:从绑定事件开始
props 适用于
a. 父组件 ==> 子组件 通信
b. 子组件 ==> 父组件 通信(要求父组件先给子组件一个函数)
使用 v-model 时要切记: v-model 绑定的值不能是 props 传过来的值,因为 props 是不可以修改的
props 传过来的若是对象类型的值,修改对象中的属性时 Vue 不会报错,但不推荐这样做
Vue CLI 本地存储 自定义事件
WebStorage(js 本地存储)
存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制
相关API
xxxStorage.setItem('key', 'value') 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,
则更新其对应的值
xxxStorage.getItem('key') 该方法接受一个键名作为参数,返回键名对应的值
xxxStorage.removeItem('key') 该方法接受一个键名作为参数,并把该键名从存储中删除
xxxStorage.clear() 该方法会清空存储中的所有数据
备注
SessionStorage 存储的内容会随着浏览器窗口关闭而消失
LocalStorage 存储的内容,需要手动清除才会消失
xxxStorage.getItem(xxx) 如果 xxx 对应的 value 获取不到,那么 getItem() 的返回值是 null
JSON.parse(null) 的结果依然是 null
组件的自定义事件
- 一种组件间通信的方式,适用于:子组件 ===> 父组件
- 使用场景:子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件(事件的回调在A中)
绑定自定义事件
a. 第一种方式,在父组件中<Demo @事件名="方法"/> 或 <Demo v-on:事件名="方法"/>
b. 第二种方式,在父组件中this.$refs.demo.$on('事件名',方法)
<Demo ref="demo"/> ...... mounted(){ this.$refs.demo.$on('baiye',this.test) }
c. 若想让自定义事件只能触发一次,可以使用 once 修饰符,或 $once 方法
- 触发自定义事件 this.$emit('事件名',数据)
- 解绑自定义事件 this.$off('事件名')
- 组件上也可以绑定原生 DOM 事件,需要使用 native 修饰符 @click.native="show"
上面绑定自定义事件,即使绑定的是原生事件也会被认为是自定义的,需要加 native ,加了后就将此事件给组件的根元素 - 注意:通过 this.$refs.xxx.$on('事件名',回调函数) 绑定自定义事件时,回调函数要么配置在 methods 中,要么用箭头函数,否则 this 指向会出问题
Vue CLI $nextTick 过渡与动画
1. $nextTick
这是一个生命周期钩子
this.$nextTick(回调函数) 在下一次 DOM 更新结束后执行其指定的回调
什么时候用:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行
2. 过渡与动画
Vue 封装的过度与动画:在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名
写法
1. 准备好样式
元素进入的样式
ⅰ. v-enter 进入的起点
ⅱ. v-enter-active 进入过程中
ⅲ. v-enter-to 进入的终点
元素离开的样式
ⅰ. v-leave 离开的起点
ⅱ. v-leave-active 离开过程中
ⅲ. v-leave-to 离开的终点
- 使用
包裹要过度的元素,并配置 name 属性,此时需要将上面样式名的 v 换为 name 要让页面一开始就显示动画,需要添加 appear
<transition name="hello" appear> <h1 v-show="isShow">你好啊!</h1> </transition> <style> .hello-enter-active{ animation: hello 0.5s linear; } .hello-leave-active{ animation: hello 0.5s linear reverse; } @keyframes hello { from{ transform: translateX(-100%); } to{ transform: translateX(0px); } } </style>
4. 备注:若有多个元素需要过度,则需要使用
,且每个元素都要指定 key 值 <transition-group name="hello" appear> <h1 v-show="!isShow" key="1">你好啊!</h1> <h1 v-show="isShow" key="2">尚硅谷!</h1> </transition-group>
5. 第三方动画库 Animate.css
<transition-group appear name="animate__animated animate__bounce" enter-active-class="animate__swing" leave-active-class="animate__backOutUp"> <h1 v-show="!isShow" key="1">你好啊!</h1> <h1 v-show="isShow" key="2">尚硅谷!</h1> </transition-group>
Vue slot 插槽
slot 插槽<slot>
插槽:让父组件可以向子组件指定位置插入 html 结构,也是一种组件间通信的方式,
适用于 父组件 ===> 子组件
- 分类:默认插槽、具名插槽、作用域插槽
使用方式
a. 默认插槽父组件中: <Category> <div>html结构1</div> </Category> 子组件中:Category <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template
b. 具名插槽
父组件指明放入子组件的哪个插槽slot="footer"
,如果是template
可以写成v-slot:footer
父组件中: <Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot:footer> <div>html结构2</div> </template> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template>
c. 作用域插槽
scope
用于父组件往子组件插槽放的 html 结构接收子组件的数据
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
将不带参数的 v-slot 假定为对应的默认 slot
可以使用v-slot="slotProps"
替代v-slot:default="slotProps"
有多个 slot 的时候,不能将 v-slot 添加到标签上
<nav-head v-slot="slotProps">
{{ slotProps.data.key }}
</nav-head>
个人理解:
如果在子组件定义了一个默认插槽,父组件想传入到这个插槽的结构只有一个html标签的话
可以省略template标签!
如果默认插槽要返回参数给父组件(作用域插槽)就要用template标签来接收
还有一种情况,如果在父组件中想传到默认插槽位置的结构不止一个html标签,也要带上template标签
可以使用 v-slot="slotProps"` 替代 `v-slot:default="slotProps"
v-slot:defalut 就是在父组件匹配子组件中的默认插槽
注意:关于样式,既可以写在父组件中,解析后放入子组件插槽;也可以放在子组件中,传给子组件再解析
Vuex
1.Vuex是什么
概念:专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 Vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
2.什么时候使用 Vuex
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
3.. Vuex 工作原理图
4.. 搭建 Vuex 环境 - 下载安装 vuex npm i vuex
创建 src/store/index.js 该文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue' import Vuex from 'vuex' // 引入Vuex Vue.use(Vuex) // 应用Vuex插件 const actions = {} // 准备actions——用于响应组件中的动作 const mutations = {} // 准备mutations——用于操作数据(state) const state = {} // 准备state——用于存储数据 // 创建并暴露store export default new Vuex.Store({ actions, mutations, state, })
3. 在 src/main.js 中创建 vm 时传入 store 配置项
import Vue from 'vue' import App from './App.vue' import store from './store' // 引入store Vue.config.productionTip = false new Vue({ el: '#app', render: h => h(App), store, // 配置项添加store beforeCreate() { Vue.prototype.$bus = this } })
4.使用 Vuex 编写
Vuex的基本使用:- 初始化数据 state ,配置 actions 、 mutations ,操作文件 store.js
- 组件中读取 vuex 中的数据 $store.state.数据
组件中修改 vuex 中的数据 $store.dispatch('action中的方法名',数据)
或 $store.commit('mutations中的方法名',数据)
若没有网络请求或其他业务逻辑,组件中也可越过 actions ,即不写 dispatch ,直接编写 commit四个map方法的使用
*1.mapState方法:用于帮助映射
state
中的数据为计算属性computed: { // 借助mapState生成计算属性:sum、school、subject(对象写法一) ...mapState({sum:'sum',school:'school',subject:'subject'}), // 借助mapState生成计算属性:sum、school、subject(数组写法二) ...mapState(['sum','school','subject']), },
2.mapGetters方法:用于帮助映射
getters
中的数据为计算属性computed: { //借助mapGetters生成计算属性:bigSum(对象写法一) ...mapGetters({bigSum:'bigSum'}), //借助mapGetters生成计算属性:bigSum(数组写法二) ...mapGetters(['bigSum']) },
3.mapActions方法:用于帮助生成与
actions
对话的方法,即包含`$store.dispatch(xxx)的函数methods:{ //靠mapActions生成:incrementOdd、incrementWait(对象形式) ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) //靠mapActions生成:incrementOdd、incrementWait(数组形式) ...mapActions(['jiaOdd','jiaWait']) }
4.mapMutations方法:用于帮助生成与
mutations
对话的方法,即包含$store.commit(xxx)
的函数methods:{ //靠mapActions生成:increment、decrement(对象形式) ...mapMutations({increment:'JIA',decrement:'JIAN'}), //靠mapMutations生成:JIA、JIAN(对象形式) ...mapMutations(['JIA','JIAN']), }
注意:mapActions
与mapMutations
使用时,若传递参数需要:在模板中绑定事件时传递好参数,否
则参数是事件对象
模块化+命名空间
1.目的:让代码更好维护,让多种数据分类更加明确
2.修改store.js
为了解决不同模块命名冲突的问题,将不同模块的namespaced:true
,之后在不同页面中引入getter
`actions`mutations
时,需要加上所属的模块名
const countAbout = {
namespaced: true, // 开启命名空间
state: {x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){ return state.sum * 10 }
}
}
const personAbout = {
namespaced: true, // 开启命名空间
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
3.开启命名空间后,组件中读取state
数据
// 方式一:自己直接读取
this.$store.state.personAbout.list
// 方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
4.开启命名空间后,组件中读取getters
数据
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
5.开启命名空间后,组件调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
6.开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
v-model是语法糖
语法糖:对一更加复杂的操作的封装,讨好程序员的.
// 1. v-model 在表单元素上使用
<input v-model="xxx" />
// 2. v-model 在自己定义的组件上使用
<MyCom v-model="xxx" />
上面的写法是快捷方式,它等价于如下复杂的写法:
<MyCom :value="xxx" @input="新值=>xxx=新值" />
// v-model做两件事:
// 1. 向子组件传来一个名为value的属性
// 2. 在子组件监听input事件,这个事件的回调中修改value所绑定的值
问:为啥要在自己的组件上用v-model?
答:v-model写法比较简单,一个指令实现两个功能:子传父(input事件),父传子(value属性)。
问:可不可以不用?
答:可以。
vue2响应式的缺点
响应式:数据改变-->视图跟着变
对象新增的属性没有响应式
数组的部分操作没有响应式
main.js 批量注册指令
1.按需导入时的重命名, 使用 as
进行重命名
import * as results from './xxx.js'
2.批量注册用 Object.keys (推荐) 遍历对象, 优先用Object.keys, 先转数组
import * as directives from '@/directives'
// 批量注册全局的自定义指令
Object.keys(directives).forEach(key => {
Vue.directive(key, directives[key])
})
针对上面的引入语法 import * as 变量
得到的是一个对象{ 变量1:对象1,变量2: 对象2 ... }
,
可以采用 Object.keys 得到一个对象键的数组, 进行遍历处理
main.js 批量注册组件
利用 Vue.use
统一全局注册组件
- Vue.use 可以接收一个对象, Vue.use(obj)
- 对象中需要提供一个 install 函数
- install 函数可以拿到参数 Vue, 且将来会在 Vue.use 时, 自动调用该 install 函数
提供统一注册的入口文件src/componets/index.js
// 该文件负责所有的公共组件的全局注册
// vue插件机制: Vue.use
// Vue.use(params)方法中可以params传递函数或对象作为参数
// params 一般是一个对象 对象中有一个方法 名称叫install
// 1.当Vue.user(obj) install 这个方法会自动地执行
// 2.install方法中有形参 这个形参时Vue
import PageTools from './PageTools'
export default {
install(Vue) {
Vue.component('PageTools', PageTools)
}
}
在入口处进行注册 src/main.js
Vue.use 注册使用自己的插件模块
import Components from './components'
Vue.use(Components)
最新回复