vue
- 渐进式JavaScript框架,拥有自己规则的语法
- 官网地址(作者:尤雨溪)
渐进式
- 逐步增加需求功能,逐步集成
库和框架
- 库:封装的属性或方法(axios)
- 框架:拥有自己的规则和元素,比库强大得多(bootstrap, vue)
开发方式
- 传统开发方式:基于html文件开发vue
- 工程化开发方式:在webpack环境中开发Vue(常用)
@vue/cli和脚手架介绍
- 脚手架:保证施工过程顺利进行的工作平台,开箱即用(搭建项目)
- @vue/cli:Vue官方提供的一个全局模块包(得到vue命令),用于创建脚手架项目
安装@vue/cli
-
全局安装
npm install -g @vue/cli -
查看版本
vue -V
创建项目
-
创建项目((项目名不能有大写字母,中文和特殊符号))
vue create xxx -
选择模板和包管理器,等待脚手架项目创建完毕(上下键回车选择版本)
启动开发服务
-
cd进入自定义项目下,启动内置的webpack本地热更新开发服务器 - 浏览项目页面
cd xxxnpm run serve -
可以手动打开端口,查看浏览器页面
清理脚手架欢迎界面
- assets 和 components文件夹下的内容全部删除
- src/App.vue默认有很多内容,全部删除留下template, script 和 style的框
项目主要文件
- node_modules——项目npm依赖包
- index.html——浏览器运行的网页
- main.js——项目入口
- App.vue——页面根组件,渲染入口
- package.json——记录项目依赖包声明
配置端口号
-
在vue.config.js文件里面添加配置
devServer:{ // 自定义服务配置 port:3000, open:true //某些平台会出错 } -
重新在项目下运行命令
npm run serve
eslint检查代码
- 代码检查工具,替代jslint
- 如果写代码违反eslint规则-报错(详细)
处理代码检查
-
手动解决错误/自动解决
-
暂时关闭eslint检查
lintOnSave: false //关闭检查
vue单文件
- Vue推荐采用.vue文件来开发项目
- template里面只能有一个根标签
- vue文件——独立模块——作用域互不影响
- style配合scoped属性,保证样式只在当前tamplate内标签生效,否则默认全局样式
- vue文件配合webpack,打包插入到index.html
基础-插值表达式
作用:在dom标签中,直接插入vue数据变量(动态显示)
-
别名:声明式渲染/文本插值
-
语法:
{{ 表达式 }}<template> <div> <h2>{{ msg }}</h2> </div> </template>
-
-
表达式和语句的区别
- 语句:if语句,for语句
- 表达式:
- 变量
- 属性
- 三元运算符
- 方法调用
- 数字、字符串
-
vue数据变量
-
需要在js的data函数里声明
-
data里面必须返回一个对象
export default { data() { return { msg: "hello鸭,你好哇", }; }, };
-
MVVM设计模式
- 转变思维,用数据驱动视图改变,操作dom的事情,vue源码做了
Vue指令
v-bind
基本使用
给标签属性设置vue变量的值
-
语法
-
写法:
v-bind:属性名="vue变量" -
简写:
:属性名="vue变量"(常用)<img v-bind:src="imgSrc" alt="" /> <a :href="baidu">baidu</a>
-
动态设置样式(v-bind)
css
-
语法:
:class="{类名: 布尔值}"<button :class="{ 'change-color': flag, fs: !flag }" @click="fn">按钮</button>export default { data() { return { flag: false, }; }, methods: { fn() { this.flag = !this.flag; }, }, }; -
**注意:**类名如果包含横杠,用单引号包裹
style
-
语法:
:style="{css属性名: 值}"<button :style="{ 'background-color': 'red', fontSize: '12px' }" @click="fn">按钮111</button> -
**注意:**样式名如果包含横杠,用单引号包裹,或者写成小驼峰
v-on
给标签绑定事件
语法
-
v-on:事件名="少量代码" -
v-on:事件名="method中的函数名" -
v-on:事件名="method中的函数名(实参)"<button v-on:click="count++">加1</button> <button v-on:click="calc">+ 5</button> <button v-on:click="calcNew(20)">+ 20</button> -
简写:
@事件名="methods中的函数"<button @click="calcNew(50)">+ 50</button> -
函数在methods选项里定义
export default { methods: { calc() { this.count += 5; }, calcNew(num) { this.count += num; }, }, };
v-on事件对象
-
语法
-
没有传参,通过形参接收
<a :href="baidu" @click="fn">点我</a>fn(e) { e.preventDefault(); }, -
传参,通过
$event指代事件对象传给事件处理函数$event固定写法,不可更改
<a :href="baidu" @click="fn1(12,$event)">点我</a>fn1(num,e) { e.preventDefault(); },
-
v-on修饰符
事件.修饰符——带来更强大的功能
-
语法
- @事件名.修饰符="methods对象里面的函数"
-
修饰符
-
.stop——阻止事件冒泡
<div @click="say('father')"> father <div @click.stop="say('son')">son</div> </div>say(dom) { console.log(`i am ${dom}`); }, -
.prevent——阻止默认行为
<a href="javascript:;" @click.prevent="fn">url</a>fn() { console.log("阻止默认行为"); }, -
.once——程序运行期间,只触发一次事件处理函数
<button @click.once="fn1">我只会触发一次</button>fn1() { console.log("触发一次解除绑定"); },
-
v-on按键修饰符
给键盘事件,添加修饰符,增强能力
-
语法
-
@keydown.enter
<div>密码:<input type="password" @keydown.enter="login" /></div>login(){ console.log("触发了"); }
-
v-model
value属性和vue数据变量,双向绑定
-
语法:
v-model="Vue数据变量" -
双向数据绑定
-
变量变化 ---> 视图自动同步
-
视图变化 ---> 变量自动同步
-
表单标签常用
- 复选框
- v-model默认绑定checked属性
- 可以将vue变量初始化为空数组,绑定的是选中项value
- 单选按钮
- 不需要设置为相同name属性
- 绑定选中项value
- 下拉列表
- v-model写在select标签中
- 绑定的是option的value
<div> <h5>{{ username }}</h5> <div> 用户名: <input type="text" v-model="username" /> </div> <div> 密码: <input type="password" v-model="password" /> </div> <button @click="login">login</button> </div><template> <div> <!-- 下拉列表 --> <div> 城市: <select v-model="city"> <option value="湛江">湛江</option> <option value="茂名">茂名</option> <option value="广州">广州</option> </select> </div> <!-- 复选框 --> <div> 爱好: <input type="checkbox" value="音乐" v-model="hobby" />音乐 <input type="checkbox" value="跳舞" v-model="hobby" />跳舞 <input type="checkbox" value="唱歌" v-model="hobby" />唱歌 </div> <!-- 单选按钮 --> <div> 性别: <input type="radio" value="男" v-model="gender" />男 <input type="radio" value="女" v-model="gender" />女 </div> <!-- 文本框 --> <div> 自我介绍: <textarea v-model="txt"></textarea> </div> <div> <button @click="getData">获取数据</button> </div> </div> </template> <script> export default { data() { return { city: "", hobby: [], gender: "", txt: "", }; }, methods: { getData() { console.log(this.city, this.hobby, this.gender, this.txt); }, }, }; </script> <style> </style> - 复选框
-
v-model修饰符
让v-model拥有强大功能
-
语法:
v-model.修饰符="Vue数据变量"-
.number——以parseFloat转成数字类型
-
.trim——去除首尾空白字符
-
.lazy——在change时触发而非input时(失去焦点,随后更新变量)
<template> <div> <div> 自动转换数字 <input type="text" v-model.number="say" /> </div> <div> 去除空格 <input type="text" v-model.trim="username" /> </div> <div> 节流 <input type="text" v-model.lazy="pwd" /> </div> </div> </template> <script> export default { data() { return { say: "", username: "", pwd: "", }; }, methods: {}, }; </script> <style> </style>
-
v-for
作用:列表渲染,所在标签结构,按照数据数量,循环生成
-
语法
-
v-for="值变量 in 目标结构"
<ul> <li v-for="item in students">{{ item }}</li> </ul>students: ["杰豪", "老王", "万少"], -
v-for="(值变量, 索引变量) in 目标结构"
<ul> <li v-for="(item, idx) in food">{{ idx + 1 }}{{ item }}</li> </ul>food: ["姜葱鸡", "番茄炒蛋", "青椒包肉"],
-
-
目标结构:
-
可以遍历数组/对象/数字/字符串(可遍历结构)
-
数字:从1开始遍历到当前数字结束
-
对象:可以遍历(value,key)
-
字符串:逐步把每一个字符遍历
<!-- 遍历数字 --> <ul> <li v-for="(item, idx) in num">{{ item }}</li> </ul> <!-- 遍历对象 --> <ul> <li v-for="(item, key) in obj">{{ key }}:{{ item }}</li> </ul> <!-- 遍历字符串 --> <ul> <li v-for="(item, idx) in str">{{ item }}</li> </ul>num: 10, obj: { name: "楼下小黑", age: 24, phone: "1300000000", }, str: "前端与移动开发75期班级",
-
-
注意
- v-for的临时变量名不能用到v-for范围外
v-show和v-if
控制标签显示隐藏
-
语法
-
v-show="Vue变量"<p v-show="age > 18">我18了</p> <p v-show="age < 18">我未成年</p> -
v-if="Vue变量"<!-- 不满足条件,直接删除整个元素 --> <p v-if="age > 18">我18了</p> <p v-if="age < 18">我未成年</p>
-
-
原理
- v-show——通过
display:none隐藏标签(需要频繁切换渲染的时候使用) - v-if——不满足条件直接从DOM树上移除标签
- v-show——通过
-
高级
-
v-if、v-else、v-else-if
-
三者搭配使用,标签之间不可以在使用其他语法
<template> <div> <div> <input type="text" v-model.number="age" /> </div> <p v-if="age < 18">我未成年</p> <p v-else-if="age = 18">我成年</p> <p v-else-if="age < 50">我小于五十</p> <p v-else-if="age < 100">我快一百岁了</p> <p v-else>我长寿村长大</p> </div> </template> <script> export default { data() { return { age: "", }; }, }; </script> <style> </style>
-
v-text/v-html
更新DOM对象的innerText/innerHTML
-
语法
-
v-text="Vue变量"<p v-text="text"></p> -
v-html="Vue变量"<p v-html="liHtml"></p>data() { return { liHtml: "<button>我是按钮</button>", text: "我是纯文本", };
-
-
**注意:**会覆盖插值表达式
案例
翻转句子案例
-
思路
-
将文本内容通过
split()方法分割为数组 -
再用数组方法
reverse()将数组元素自动翻转 -
赋值回去给文本容器
<div> <p>{{ msg }}</p> <button @click="fn">我要翻天</button> </div>methods: { fn() { const arr = this.msg.split(""); this.msg = arr.reverse().join(""); }, },
-
折叠面板案例
<template>
<div>
<h1>
折叠面板
<button @click="show">{{ flag ? "收起面板" : "展开面板" }}</button>
</h1>
<div>
<h3>静夜思</h3>
<div v-show="flag">
<p>床前明月光</p>
<p>床前明月光</p>
<p>床前明月光</p>
<p>床前明月光</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
flag: true,
};
},
methods: {
show() {
this.flag = !this.flag;
},
},
};
</script>
<style>
</style>
更新检测
数组更新
- 数组方法修改原始数组,会触发页面刷新
reverse()——就地更新机制,需要添加key优化
- 数组方法不修改原始数组,不会触发页面刷新
- 通过索引修改原始数组,不会触发页面刷新
- 可以通过Vue给
this注入的**$set方法**修改数组- 语法:
$set(数组, index, 修改后的值)
- 语法:
- 可以通过Vue给
就地更新机制
- 数据对比更新,消耗性能
- 优化
- 给列表内容加上
key(唯一标识) - 更新DOM时,会根据
key比较内容,不会触发就地更新
- 给列表内容加上
虚拟DOM
本质是保存节点关键信息的JS对象
过滤器
转换格式,是一个函数,传入值返回处理后的值
-
过滤器只能在插值表达式和v-bind动态属性里使用
-
Vue过滤器场景
- 字符串翻转
- 字母转大写
-
语法
-
Vue.filter("过滤器", value => {return "处理后的值"}(全局写法,且要在main.js文件写,因为需要一个个声明过滤器,所以不加s)<p>转换大写:{{ msg | toUpper }}</p>// 全局过滤器 Vue.filter("toUpper", (str) => { return str.toUpperCase(); }); -
filters:{过滤器名字(value) => {return "处理后的值"}}<p>价格:{{ price | initPrice }}</p> <p>转换大写:{{ msg | toUpper }}</p>export default { data() { return { price: 239, msg: "hello", }; }, methods: {}, filters: { initPrice(num) { return `${num}¥`; }, }, };
-
过滤器传参、多过滤器
可同时使用多个过滤器,或者传参
-
语法
-
传参:vue变量 | 过滤器(实参)
<p>价格:{{ price | initPrice("$","wait to buy") }}</p>filters: { initPrice(num,format,str) { return `${num} ${format} <--- ${str}`; }, }, -
多过滤器:vue变量 | 过滤器1 | 过滤器2
<p>转换大写:{{ msg | toUpper | reverseFn }}</p>// 过滤器 Vue.filter("toUpper", (str) => { return str.toUpperCase(); }); // 过滤器 Vue.filter("reverseFn", (str) => { console.log(str); return str.split("").slice(0).reverse().join(""); });
-
时间格式化(moment包)
创建时间以YYYY-MM-DD格式显示
- 在哪个文件使用,就在哪个文件导入moment包
计算属性computed
根据一些数据计算而来的结果
-
语法
-
<p>总价:{{ sum }}={{ a }}+{{ b }}</p> <input type="text" v-model.number="a" />export default { data() { return { a: 10, b: 20, }; }, computed: { sum() { return this.a + this.b; }, }, };
-
-
作用:计算属性依赖的数据变化是,计算属性会重新运算
-
**注意:**计算属性也是vue数据变量,所以不要和data里的数据重名,不要使用括号语法
-
默认情况下,计算属性,只知道怎么取值
-
改变计算属性,它还不知道怎么办
-
所以需要加上set函数
- 主动改变计算属性的时候,需要设置set让计算属性知道该怎么处理改动的值
完整写法——计算属性
-
语法
computed:{ "属性名":{ get(){ return 值 }, set(值){ } } }
缓存
- 计算属性,基于依赖项的值进行缓存,依赖的变量不变,都直接从缓存取结果
侦听器watch
侦听data/computed属性值的改变
-
作用:侦听数据改变前后(获得新旧数据)
-
语法
watch:{ "被侦听的属性名"(newVal,oldVal){ } }
深度侦听和立即执行
侦听复杂类型,立即执行侦听函数
-
语法
user: { // 立即执行函数 // 深度侦听复杂类型,必须设置 deep: true, handler(newVal, oldVal) { }, },
组件
可复用的Vue实例,封装标签,样式和JS代码
- 组件化——封装思想
- 把页面上可用的部分或者拆分部分封装为组件,从而方便项目的开发和维护
- 各自独立,互不影响
基础使用
组件都是独立个体,代码里体现为一个独立的.vue文件
-
步骤
-
创建组件,封装要复用的标签,样式,JS代码
-
注册组件
-
全局注册——main.js文件内
import 组件对象 from 'vue文件路径' Vue.component("组件名", 组件对象) -
局部注册——.vue文件内
import 组件对象 from 'vue文件路径' export default { component:{ "组件名": 组件对象 } }
-
-
使用组件
<template> <div id="app"> <h3>折叠面板</h3> <组件名></组件名> <组件名></组件名> </div> </template>
-
独立样式
- scoped
- 避免组件样式互相污染
- 注意
- 继承不受影响
- 根元素不受影响
- 权重问题
组件通信-父传子
父组件 ==> 子组件 传值
- 首先明确父与子是谁,在父文件引入子文件(引入子)
- 父亲:App.vue
- 儿子:MyProduct.vue
- 创建MyProduct.vue
- 步骤
- 子组件内,定义变量,准备接收,然后使用变量
- 父组件内,引入组件,注册组件,使用组件,传值进去
组件通信-父向子-配合循环
父组件 => 子组件 循环使用-传值
单向数据流
子组件修改父组件传递过来的数据,会报错
- 原因
- 子组件修改,不通知父组件,造成数据不一致性
- Vue规定props里的变量,本身是只读的
- 结论
- 从父组件到子组件的数据流向,称为单向数据流
子传父-自定义事件
子组件中自定义事件方法
- 子组件内,当用户点击按钮,自定义一个事件,并将数据通过该事件传递出去。
- 父组件内,绑定子组件中的自定义事件和事件处理函数
- 语法:
@自定义事件名="父methods里函数名"
- 语法:
生命周期-钩子函数
Vue框架内置函数,随着组件的生命周期阶段自动执行
- 作用:特定时间,特定操作
- 分类:4个阶段8个方法(前后)
- 初始化
- beforeCreate
- created(数据data获取完毕,还没能获取真是DOM)
- 挂载
- beforeMount
- mounted(页面渲染完毕,与window.onload相似)
- 更新
- beforeUpdate
- updated(数据更新完毕,可获取更新后的DOM)
- 销毁
- beforeDestroy
- destroyed(手动消除计时器,定时器,全局事件)
- 应用:限时活动
- 初始化
axios
- 特点
- 专门支持客户端发送Ajax请求()
- 一种前端一部请求后端的技术
- 原理:浏览器window接口的XMLHttpRequest
- 支持服务端Node.js发送请求
- 支持Promise相关用法(.then)
- 任何官方文档提到会返回promise,直接用.then方法
- 支持请求和相应的拦截器功能
- 自动转换JSON数据
- 专门支持客户端发送Ajax请求()
- axios底层还是原生js实现,内部通过Promise封装的
uncaught错误
- 请求的时候出现uncaught错误时,说明没有
请求参数
- params
- 会在请求地址后面带上参数
- data(post专用)
- 直接将参数加入请求体
基地址
- 设置在src/utils/request.js文件中
<script>
import axios from "axios";
axios.defaults.baseURL = "http://123.57.109.30:3006";
封装全局axios
- api/books.js
import axios from "@/utils/request";
// 获取图书
export function getBooks(params) {
return axios.get("/getbooks", { params });
}
// 添加图书
export function addBook(data) {
return axios.post("/addbook", {
appkey: "7250d3eb-18e1-41bc-8bb2-11483665535a",
...data,
});
}
-
App.vue文件中引用books.js中的函数
-
methods: { getBooks() { getBooks() .then((res) => { console.log(res); }) .catch((err) => { console.log(err); }); }, addBook() { addBook(this.bookData) .then((res) => { console.log(res); }) .catch((err) => { console.log(err); }); }, },
获取DOM
通过id或者ref属性获取原生DOM
- 在mounted生命周期 – 2种方式获取原生DOM标签
-
目标标签添加
id或者ref<MyBox id="myBox" ref="MyBoxRef" /> -
通过id / ref属性获取标签
console.log(document.querySelector("#myBox")); console.log(this.$refs.MyBoxRef);
获取组件对象
ref属性获取组件对象
-
组件添加
ref<MyBox id="myBox" ref="MyBoxRef" /> -
通过ref属性获取组件对象
console.log(this.$refs.MyBoxRef);
vue-异步更新DOM
点击改data, 获取原生DOM内容
<template>
<div>
<button @click="toggle">显示输入框</button>
<div class="box" v-if="isShow" ref="BoxRef"></div>
</div>
</template>
<script>
export default {
data() {
return {
isShow: true,
};
},
methods: {
toggle() {
this.isShow = !this.isShow;
console.log(this.$refs.BoxRef);
},
},
};
</script>
<style>
.box {
width: 400px;
height: 400px;
background-color: pink;
}
</style>
- Vue更新DOM是异步的
$nextTick使用
等DOM更新后, 触发此方法里函数体执行
- 可以解决dom元素异步更新问题
<template>
<div>
<button @click="toggle" v-if="!isShow">显示输入框</button>
<input class="box" v-if="isShow" ref="search" />
</div>
</template>
<script>
export default {
data() {
return {
isShow: false,
};
},
methods: {
toggle() {
this.isShow = !this.isShow;
this.$nextTick(() => {
this.$refs.search.focus();
});
},
},
};
</script>
<style>
</style>
props
- props:[]
- 不规定传入格式
- 自己封装常用
- props:{}
- 规定传过来的规则格式
- 团队合作常用
组件进阶
动态组件
多个组件使用同一个挂载点,并动态切换
- 准备需要切换的2个组件
- 引入到App.vue注册
- 准备变量来承载要显示的“组件名”
- 设置挂载点, 使用is属性来设置要显示哪个组件
<template>
<div>
<button @click="componentsName='UserInfo'">个性签名</button>
<button @click="componentsName='UserName'">用户账号</button>
<component :is="componentsName"/>
</div>
</template>
<script>
import UserName from "@/components/UserName.vue";
import UserInfo from "@/components/UserInfo.vue";
export default {
components: {
UserName,
UserInfo,
},
data() {
return {
componentsName: "UserName",
};
},
};
</script>
<style>
</style>
组件缓存
让切换显示的组件不用来回创建销毁,提升性能和用户体验
-
vue内置的keep-alive组件包起来要频繁切换的组件
<keep-alive> <component :is="componentsName" /> </keep-alive> -
被包裹的组件没有
create和destroy钩子函数
组件激活和非激活
扩展2个新的生命周期方法
- 在子组件
export default(){}里面写上activated(){}和deactivated(){}- 被缓存包裹的组件才会生效
组件插槽
通过
slot标签,让组件内可以接受不同的标签结构显示
-
给组件插入什么标签,组件就显示什么标签
-
语法
-
组件内用占位
-
使用组件时<组件><组件/>包夹的位置,传入标签替换
<!-- PersonBox组件 --> <template> <div class="single"> <div class="left"></div> <div class="right"> <slot/> </div> </div> </template>// App.vue文件 <template> <div> <PersonBox> <button>设置</button> <button>报表</button> </PersonBox> <PersonBox> <button>详情</button> </PersonBox> </div> </template> <script> import PersonBox from "@/components/PersonBox.vue"; export default { components: { PersonBox, }, }; </script>
-
插槽默认内容
如果外面不传,可以写默认显示内容
- 用法:内放置内容,作为默认显示内容
具名插槽
一个组件内有2处以上需要外部传入标签的地方
-
语法
-
使用
name属性区分名字
-