目录
1.3.6 webpack使用加载器css-loader,style-loader
1.4 webpack开发服务器web-dev-server
> > >
1 webpack模块打包工具
1.1 webpack基本概念
1.1.1 为什么学webpack
- 减少文件数量
- 缩减代码体积
- 提高浏览器打开的速度
webpack官网地址
1.1.2 webpack的基本概述
webpack本质是,一个第三方模块包,用于分析并打包代码
- 支持所有类型文件的打包
- 支持less/sass => css
- 支持ES6/7/8 => ES5(高级的语法进行降级,兼容低版本浏览器)
- 压缩代码,提高加载速度
1.2 webpack使用步骤
1.2.1 环境准备
(1) 初始化包环境
yarn init
(2) 安装依赖包
yarn add webpack webpack-cli -D
(3) 配置script(自定义命令)
"script": {
"bulid": "webpack"
}
1.2.2 webpack的基本使用
(1) 首次打包
需求: 2个js文件 ->打包成1个js文件
分析:
①:新建src下的资源
②:add.js – 定义求和函数并导出
③:index.js – 引入add模块并使用函数输出结果
④:执行 `yarn build` 自定义命令, 进行打包 (确保终端路径在src的父级文件夹)
⑤:打包后默认生成dist和main.js, 观察其中代码
(2) 再次打包
需求:代码更多后, 如何打包呢?
分析:
①:src下新建tool/tool.js
②:定义数组求和函数导出
③:index.js – 引入tool模块的函数并使用, 打印结果
④:执行 `yarn build` 自定义命令, 进行打包 (确保终端路径在src的父级文件夹)
⑤:打包后默认生成dist和main.js, 观察其中代码
代码修改了要重新打包,且路径正确
process.env访问开发或者产品环境配置文件
1.3 webpack的配置
1.3.1 webpack的入口和出口
(1) 配置
默认入口: ./src/index.js
默认出口: ./dist/main.js
webpack配置 - webpack.config.js(默认)
-
新建src并列处, webpack.config.js
-
填入配置项
const path = require("path")
module.exports = {
entry: "./src/main.js", // 入口
output: {
path: path.join(__dirname, "dist"), // 出口路径
filename: "bundle.js" // 出口文件名
}
}
-
修改package.json, 自定义打包命令 - 让webpack使用配置文件
1.3.2 _yarn build执行流程图
1.3.3 隔行变色
步骤:
①:从0准备环境, 初始化包环境, 下载webpack和webpack-cli包, 配置自定义命令build
②:yarn下载jquery, 新建public/index.html,准备一些li标签
③:src/main.js 引入jquery, 编写功能代码
④:执行打包命令
⑤:复制public/index.html到dist/, 然后引入打包后的js, 运行网页观察效果
重点:webpack打包后的js引入到html中使用
1.3.4 webpack的插件
_html-webpack-plugin
简化了html的创建
(1) 安装
yarn add html-webpack-plugin -D
(2) webpack.config.js添加配置
1.3.5 webpack打包css问题
新建 - src/css/index.css
编写去除li圆点样式代码
(重要) 一定要引入到入口才会被webpack打包
执行打包命令观察效果
报错: 因为webpack默认只能处理js文件
1.3.6 webpack使用加载器css-loader,style-loader
css-loader 让webpack能处理css类型文件
style-loader 把css插入到DOM中
(1) 下载安装加载器
yarn add style-loader css-loader -D
(2) 配置
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...其他代码
module: {
rules: [ // loader的规则
{
test: /\.css$/, // 匹配所有的css文件
// use数组里从右向左运行
// 先用 css-loader 让webpack能够识别 css 文件的内容并打包
// 再用 style-loader 将样式, 把css插入到dom中
use: [ "style-loader", "css-loader"]
}
]
}
}
总结: 万物皆模块, 引到入口, 才会被webpack打包, css打包进js中, 然后被嵌入在style标签插入dom上
1.3.7 webpack使用加载器less-loader
(1) 安装
yarn add less less-loader -D
(2) 配置
module: {
rules: [ // loader的规则
// ...省略其他
{
test: /\.less$/,
// 使用less-loader, 让webpack处理less文件, 内置还会用less翻译less代码成css内容
use: [ "style-loader", "css-loader", 'less-loader']
}
]
}
1.3.8 webpack处理图片文件
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
直接入口文件导入打包会报错,因为webpack无法自己处理图片文件
webpack5, 使用asset module技术实现字体文件和图片文件处理, 无需配置额外loader
以前用url-loader和file-loader来处理 现在webpack.config.js – 针对图片文件设置type: “assets“
再次打包观察效果 小于8KB文件, 文件转base64打包在js中, 大于8KB, 文件自动命名输出到dist下, 打包观察效果和2图区别
1.3.9 webpack加载文件的优缺点
图片翻译成了base64,放在js文件中
好处:浏览器不用发请求了,直接可以读取, 速度快
坏处: 图片太大,再转`base64`就会让图片的体积增大 30% 左右, 得不偿失
yarn install下载package.json中的所有包
1.3.10 加载字体图标
src/assets 下放入fonts字体相关文件夹(预习资料里)
src/main.js 引入 assets/fonts/iconfont.css
src/main.js 创建一个i标签, 使用字体图标标签添加到body上
添加针对字体文件的加载器规则, 使用asset/resource(直接输出文件并配置路径)
打包后运行网页观察效果
1.3.11 webpack对JS语法降级加载器
babel官网: https://www.babeljs.cn/
babel-loader文档: https://webpack.docschina.org/loaders/babel-loader/
- babel: 一个javascript编译器, 把高版本js语法降级处理输出兼容的低版本语法
- babel-loader: 可以让webpack转译打包的js代码
- 在src/main.js – 定义箭头函数, 并打印箭头函数变量 (千万不能调用, 为了让webpack打包函数, 看降级效果)
// 高级语法
const fn = () => {
console.log("你好babel");
}
console.log(fn) // 这里必须打印不能调用/不使用, 不然webpack会精简成一句打印不要函数了/不会编译未使用的代码
// 没有babel集成时, 原样直接打包进lib/bundle.js
// 有babel集成时, 会翻译成普通函数打包进lib/bundle.js
- 下载加载器
yarn add -D babel-loader @babel/core @babel/preset-env
- 配置到webpack.config.js上
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/, // 不去匹配这个文件夹下的js文件
use: {
loader: 'babel-loader', // 使用这个loader处理js文件
options: { // 加载器选项
presets: ['@babel/preset-env'] // 预设:转码规则(用bable开发环境本来预设的)
}
}
}
- 打包观察是否降级
1.4 webpack开发服务器web-dev-server
每次修改代码,重新yarn build打包,才能看到最新的效果,实际工作中,打包yarn build非常耗时
原因:
从0构建依赖
磁盘读取对应的文件到内存,webpack开始加载
再用对应的loader进行处理
将处理完的内容,输出到磁盘指定目录
解决:
起一个开发服务器,缓存一些已经打包的内容,只重新打包修改的文件,最终运行在内存中给浏览器使用
总结:
webpack开发服务器,把代码运行在内存中,自动更新,实时返回给浏览器显示
1.4.1 webpack-dev-server模块的使用
(1) 下载模块包
yarn add webpack-dev-server -D
(2) 自定义webpack开发服务器启动命令server-在package.json里
"script": {
"build": "webpack",
"server": "webpack serve"
}
(3) 启动当前工程里的webpack开发服务器
yarn server
(4) 重新编写代码,观察控制台和浏览器是否自动和更新
1.4.2 webpack开发服务器改变配置
(1) 配置端口号devServer.port
webpack.config.js中
devServer: {
port: 3000
}
2 Vue基础
2.1 Vue 的基本概念
2.1.1 为什么学vue
- 开发速度快,更高效
- 企业开发都在使用
- 前端工程师必备
(1) Vue是什么
渐进式的js框架,一套拥有自己规则的语法
官网地址:http://cn.vuejs.org/ (作者:尤雨溪)
(2) Vue里的渐进式概念
Vue从基础开始,会循序渐进向前学习,可以逐渐进行使用
(3) 库和框架
库: 封装的属性或方法(例如jQuery)
框架: 拥有自己的规则和元素,比库强大的多(Vue)
2.1.2 Vue学习方式
传统开发方式: 基于html文件开发Vue
工程化开发方式: 在webpack环境中开发Vue,这是最推荐最常用的
2.2 @vue/cli脚手架工程化
@vue/cli是Vue官方提供的一个全局模块包(得到vue命令), 此包用于创建脚手架项目
脚手架是为了保证各施工过程顺利进行而搭设的工作平台
2.2.1 脚手架的好处
开箱即用
0配置webpack
babel支持
css, less支持
开发服务器支持
2.2.2 安装
(1) 安装
yarn global add @vue/cli
(2) 查看是否成功
vue -V //显示脚手架的版本
2.2.3 创建项目
(1) 创建项目
vue create vuecli-demo 注意: 项目名称不能有大写字母,中文和特殊字符
(2) 选择模板和包管理器,等待脚手架项目创建完成
(3) 启动开发服务器
cd 进入项目下,启动内置的webpack本地热更新开发服务器-浏览器项目页面
cd vuecli-demo
yarn serve
2.2.4 脚手架目录和代码分析
2.2.5 项目架构了解
目标: 知道项目入口,以及代码执行顺序和引入关系
main.js和App.vue, 以及index.html作用和关系?
- main.js – 项目打包主入口 – Vue初始化
- App.vue – Vue页面主入口
- index.html – 浏览器运行的文件
- App.vue => main.js => index.html
2.2.6 脚手架自定义配置
项目中没有webpack.config.js文件,因为Vue脚手架项目用的vue.config.js
src并列处新建vue.config.js, 填入配置, 重启webpack开发服务器
2.2.7 eslint检查代码
目标:是一种代码检查的工具
如果写代码违反了eslint的规则-报错(代码格式有没有问题,冗余)
演示: 在main.js中随便定义变量 – 不使用 – 观察eslint报错
方式1: 手动解决掉错误, 以后项目中会讲如何自动解决
方式2: 暂时关闭eslint检查(因为现在主要精力在学习Vue语法上), 在vue.config.js中配置后重启服务
module.exports = {
lintOnSave: false // 关闭exlint检查
}
2.2.8 单vue文件讲解
vue推荐采用.vue文件来开发项目
template里只能有一个根标签
js独立作用域互不影响
style配合scoped属性,保证样式只针对当前template内标签生效
vue文件配合webpack,把他们打包起来插入到index.html
(2) 插件Vetur插件(vscode插件)
代码高亮提示和智能补全
2.2.9 脚手架项目,清理欢迎页面
assets和components 文件夹下的一切都删除掉 (不要默认的欢迎页面)
src/App.vue默认有很多内容, 可以全部删除留下template和script和style的框
2.3 Vue指令
2.3.1 vue插值表达式
在dom标签中,直接插入vue数据变量
又叫: 声明式渲染/文本插值
语法: {{ 表达式(三元表达式,变量等) }}
msg和obj是vue数据变量
要在js中data函数里声明
vue中变量声明在data函数return中:变量: value
2.3.2 vue基础-mvvm设计模式
目标: 转变思维,用数据驱动视图改变,操作dom的事,vue源码内干了
设计模式: 是一套被反复使用的,多数人知晓得,经过分类编目的,代码设计经验的总结
双向数据绑定: v变了m会自动变,m变了,v会自动变
数据驱动思想: vue不推荐操作DOM,直接操作数据
好处: 减少代码数,提高开发效率
2.3.3 vue指定-v-bind
目标: 给标签属性设置vue变量的值
v-bind语法和简写
语法:v-bind:属性名="vue变量"
简写: :属性名="vue变量"
2.3.4 Vue指令-v-on
目标: 给标签绑定事件
语法
v-on: 事件名="要执行的少量代码"
v-on: 事件名=“methods中的函数名"
v-on: 事件名=“methods中的函数名(实参)"
@事件名=...简写形式
方法在methods选项定义
this指向的是export default后面的组件对象(vue创建的实例对象)(data和methods里的属性或方法都直接挂在它的身上)
2.3.5 Vue事件处理函数中,拿到事件对象
语法:
无传参,通过形参直接接受,直接在函数定义时书写e
传参,通过$event指代事件对象传给事件处理函数,后写e(调用时默认传参$event,声明时用e形参可以接受)
形参和实参需要一一对应
2.3.6 vue指令-v-on修饰符
目标: 在事件后面.修饰符名 - 给事件带来更强大的功能
语法
- @事件名.修饰符="methods里函数"
修饰符列表
- .stop - 阻止事件冒泡
- .prevent - 阻止默认行为
- .once - 程序运行期间, 只触发一次事件处理函数(事件还触发,只是事件处理函数只执行一次)
2.3.7 按键修饰符
目标: 给键盘事件,添加修饰符,增强能力(keydown和keyup用法一致)
语法:
@keyup.enter - 监测回车按键
@keyup.esc - 监测返回按键
更多修饰符: https://cn.vuejs.org/v2/guide/events.html
2.3.8 v-model双向绑定(相当于mvvm)
value属性和vue数据变量,双向绑定到一起
语法: v-model="Vue数据变量"
双向数据绑定(双向指定的value属性-vue变量,暂时只能用在表单标签,后期可以用在组件中)
变量变化 -> 视图自动同步
视图变化 -> 变量自动同步
vue变量的初始值会影响表单的默认数据
(2) 复选框,单选框,下拉菜单,文本域的用法
- 下拉菜单: v-model要绑定在select上,当选择相应的选项时,option项的value就绑定给变量
- 复选框: v-model直接书写在标签上,当我们遇到复选框时,v-model的变量如果是非数组,它关联的是复选框checked属性(true和false),数组类型时,关联的是复选框的value属性
- 单选框: 当写的变量名和表单的value属性匹配,可以实现默认绑定
- 文本域: 文本域不用书写value,在标签内部书写内容
(3) v-model内部默认的绑定的是input事件,绑定的属性名是value
v-model的本质:v-bind(变量改变标签使用的变量跟随变化(属性名)) + v-on(标签事件改变时定义变量跟随改变(事件名))
可以使用model来修改model底层绑定的事件名和属性名(当一个组件只能使用一次,有很多次时可以使用.sync修饰符)
说到底就是需要在value值改变后将它赋值给中间变量,而value又是绑定在此变量上,所以就实现了数据的双向绑定
2.3.9 v-model修饰符
语法: v-model.修饰符
- .number 让parseFloat(只转数字样式的字符串)转成数字类型赋值给Vue变量
- .trim 去除首尾空白字符(不去除中间的空格)
- .lazy 在change(失去焦点内容改变时)时触发而非input时
2.3.10 Vue指令v-text和v-html
语法:
- v-text="Vue数据变量",用的较多
- v-html="Vue数据变量",用的较少,因为有安全风险
注意: 会覆盖插值表达式
插值表达式和v-text用法相似
2.3.11 v-show和v-if
控制标签的隐藏和出现
语法:
- v-show="Vue变量"
- v-if="Vue变量" //两者都可以起到隐藏和出现的效果
- Vue变量: true //显示, false//不显示
原理:
- v-show隐藏的方式采用的是display:none // 频繁切换的时候
- v-if隐藏的方式采用的是DOM树直接移除 // 移除的时候
高级:
- v-else: 和if else语法类似,和v-if互斥
2.3.12 Vue指令v-for
列表渲染, 所在标签结构, 按照数据数量, 循环生成
语法:
- v-for="(值变量, 索引变量) in 目标结构"
- v-for="值变量 in 目标结构"
注意:
- 想要谁循环就放在谁身上(根据遍历到的数据动态生成标签)
- 目标结构可以是数组/对象/字符串(可遍历结构)
- 循环都是独立执行的
- v-for的临时变量名只能用在v-for范围内
3 Vue基础(key, 计算属性, 过滤器, 侦听器)
3.1 Vue基础 _更新监测, key作用
目标结构变化, 触发v-for的更新
数组变更方法,就会导致v-for更新,页面更新,但只改变一个值的时候不会触发v-for更新
数组的非变更方法,返回新数组,就不会导致v-for更新,可采用覆盖数组(改变多个值)或this.$set(更新的目标的结构, 更新的位置, 更新的值)
这些方法会触发数组改变, v-for会监测到并更新页面
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
这些方法不会触发v-for更新
slice()
filter()
concat()
3.1.1 v-for就地更新
新旧DOM产生后对比,然后决定复用真实DOM/更新内容(对DOM的销毁和创建减到最少)
3.1.2 虚拟DOM
真实的DOM树: HTML的节点关系
虚拟DOM:本质是保存节点的关键信息,属性和内容的一个JS对象
真实的DOM的属性太多,浏览器每次翻译标签为DOM的时候需要消耗时间
虚拟DOM的好处:
- 提高DOM的更新性能,不用频繁操作真实DOM
3.1.3 diff算法
同级比较-根元素变化-整个DOM树删除重建
vue用diff算法, 新虚拟dom, 和旧的虚拟dom比较
情况1: 根元素变了, 删除重建
情况2: 根元素没变, 属性改变, ==元素复用==, 更新属性
3.1.4 diff算法-key
情况3: 无key
最大限度尝试就地修改/复用相同类型元素
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key
来提供一个排序提示
==性能不高, 从第二个li往后都更新了==
情况4: 有key,值为索引
有key属性, 基于key的来比较新旧虚拟DOM, 移除key不存在元素
先产生新旧虚拟DOM,根据key比较,还是就地更新
新虚拟DOM的产生是根据原来数组的插入规则来的
3.1.4 有key,值唯一不重复的字符串或数字
给每个数据换成对象, 准备id, 把id的值作为key
好处: 可以配合虚拟DOM提高更新的性能
在生成虚拟DOM后,渲染真实标签的时候,diff算法会比较新旧虚拟DOM,有key会用key来进行比较,没key就地更新内容(尝试最大限度复用li标签,一般只会添加和减少li标签或修改li标签内容)
虚拟DOM的对象中子标签对象不会影响父级的diff比较
v-for循环中一般都得加:key,一般情况下用id,没id时用索引
3.1.5 动态class
用v-bind给标签class设置动态的值(提前写好class类名样式,通过动态class配置)
语法 :class="{类名: 布尔值}"
布尔值为true代表生效(默认为true)
false为不生效
3.1.6 动态style
语法 :style="{css属性名: 值}"
值是变量的时候直接写,不是变量的时候需要加单引号
3.2 过滤器
转换格式,过滤器就是一个函数,传入值返回处理后的值
过滤器只能用在,插值表达式和v-bind动态属性里
语法:
- Vue.filter("过滤器名", (值) => {return "返回处理后的值"}) // 全局过滤器,可以生效在所有的vue文件中
- filters: {过滤器名字: (值) => {return "返回处理后的值"}} // 局部过滤器,只在当前vue文件中生效
使用: 变量名 | 过滤器名
3.2.1 过滤器的更多用法
可以同时使用多个过滤器,或者给过滤器传参
语法: 过滤器传参: vue变量 | 过滤器名(参数)
多个过滤器: vue变量 | 过滤器1 | 过滤器2
3.3 计算属性 computed
一个变量的值,依赖另外一些数据计算而来的结果
语法: computed: {
"计算属性名"() {
return "值"
}
}
注意: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同,不需要调用,使用的时候和变量一致
使用this.计算属性名也可以使用该变量
计算属性将基于它们的响应依赖关系缓存。计算属性只会在相关响应式依赖发生改变时重新求值。这就意味着只要计算属性依赖值(非响应式数据获得的计算属性值将永远不发生改变)还没有发生改变,多次访问时计算属性会立即返回之前的计算结果,而不必再次执行函数。(当有需求实时的获取值得时候可以采用函数调用的形式得到值)
3.3.1 计算属性缓存
计算属性,基于依赖项的值进行缓存,依赖的变量不变,都直接从缓存取结果
计算属性的优势:
- 带缓存
- 计算属性对应函数执行后,会把return值缓存起来
- 依赖项不变,多次调用都是从缓存取值
- 依赖项值-变化,函数会"自动"重新执行-并缓存新的值
面试题补充:
计算属性和方法的区别:
计算属性带有缓存,只要计算属性中变量的值不发生变化就不会重复执行
方法不带缓存,每次调用都会执行
3.3.2 计算属性的完整写法
给计算属性赋值需要setter
// 给计算属性赋值触发set方法
// 使用计算属性的值,触发get方法
语法: computed: {
"属性名": {
set(值) {
},
get() {
return "值"
}
}
}
计算属性和data的区别:
计算属性是依据别的变量计算而来
data是直接定义来的
数组every方法: 查找数组里"不符合"条件,查找成功直接原地返回false
3.4 侦听器
可以侦听data/computed属性值的改变
语法1:
watch: {
"被侦听的属性名" (newVal, oldVal) {
}
}
高级语法:
watch: {
"要侦听的属性名": {
immediate: true, // 立即执行
deep: true, // 深度侦听复杂数据类型内变化
handler (newVal, oldVal) {}
}
}
4 组件
4.1 组件概念,创建和使用
4.1.1 组件的概念
- 组件是可复用的 Vue 实例, 封装标签, 样式和JS代码
- 组件化 :封装的思想,把页面上 `可重用的部分` 封装为 `组件`,从而方便项目的开发 和 维护
- 一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的结构 样式 和 行为(html, css和js)
- 各自独立,互不影响
4.1.2 组件的基础使用
每个组件都是一个独立的个体, 代码里体现为一个独立的.vue文件
- 创建组件, 封装要复用的标签, 样式, JS代码
- 注册组件(使用前需要引入)
- 全局注册 – main.js中 – 语法如图
- 局部注册 – 某.vue文件内 – 语法如图
- 使用组件(组件名当template标签使用,组件对象代表引入时命名的对象)
4.1.3 scoped作用
style标签上的scoped作用:
当前组件内标签都被添加data-v-hash值的属性
获取:css选择器都被添加[data-v-hash值]的属性选择器
4.2 组件通信
4.2.1 组件通信_父传子_props
父组件->子组件 传值
首先明确父和子是谁,在父引入子 (被引入的是子)
子组件内,定义变量,准备接收,然后使用变量
父组件(App.vue)内, 要展示封装的子组件(MyProduct.vue)
引入组件, 注册组件, 使用组件, 传值进去
4.2.2 组件通信_父向子_配合通讯
(1) 可以使用v-for循环组件标签吗
可以使用:
每次循环组件独立执行,互不影响
(2) props获取路由组件传参(通过props解耦)
开启props传参,说白了就是把路由参数映射到组件之中
(3) props中接收的数据可以以对象的形式接收,其中具体的项也是对象,并可以添加效验规则
4.2.3 单项数据流
从父到子的数据流向,叫单向数据流
- 子组件修改父传入的数据不通知父亲,会导致数据的不一致
- props里的变量不能修改,是只读的
- 复杂数据类型如果不修改地址,只修改其中的属性,可以无视单项数据流,父与子的数据一致
4.2.4 组件通信_子向父传值_自定义事件
子组件触发父自定义事件方法
需求:商品组件,实现砍价功能
前置补充, 父->索引->子组件(用于区分是哪个子组件)
- 父组件内,绑定自定义事件和事件处理函数
- 语法: @自定义事件名="父methods里函数名"
- 子组件内,恰当的时机,触发父给我绑的自定义事件,导致父methods里事件处理函数执行
4.2.5 组件通信_EventBus
常用于跨组件通信时使用
语法:
src/EventBus/index.js
- 新建vue实例并导出:import Vue from 'vue'; module default new Vue()
- 在要接收值的组件(List.vue) eventBus.$on('事件名', 函数体)
- 在要传递值的组件(MyProduct.vue) eventBus.$emit('事件名', 值)
- eventBus技术本质是一个空白vue对象
- 每一个vue组件都是一个单独的vue实例对象
vue中通过$.on()给vue实例注册事件,通过$.emit发送信息并告知$.on()触发事件,eventbus中,两个不相关的组件之间,都引入eventbus组件,建立联系,注册与触发事件
4.3 vue生命周期讲解
4.3.1 vue生命周期
从创建到销毁的整个过程就是-vue实例的-生命周期
4.3.2 钩子函数
Vue框架内置函数,随着组件的生命周期阶段,自动执行
作用: 特定的时间点,执行特定的操作
场景: 组件创建完毕后,可以在created生命周期函数中发起Ajax请求,从而初始化data数据
分类:4大阶段8个方法
4.3.3 钩子函数-初始化阶段
- new Vue() - Vue实例化(组件也是一个小的Vue实例)
- Init Event & Lifecycle-初始化事件和生命周期函数
- beforeCreate-生命周期钩子函数被执行
- Init injections&reactivity-Vue内部添加data和methods等
- data等中的变量挂载在vue实例上
- created-生命周期钩子函数被执行,实例创建
- 接下来是编译模板阶段-开始解析
- Has el option? – 是否有el选项 – 检查要挂到哪里
- 没有. 调用$mount()方法
- 有, 继续检查template选项
一般情况下,created使用较多,
使用场景:网络请求,注册全局事件
4.3.4 挂载阶段
- 检查template选项
- 有 - 编译template返回render渲染函数
- 无 – 编译el选项对应#app)作为template(要渲染的模板)
- 虚拟DOM挂载成真实DOM之前
- beforeMount-生命周期钩子函数执行
- Create… – 把虚拟DOM和渲染的数据一并挂到真实DOM上
- 真实DOM挂载完毕
- mounted – 生命周期钩子函数被执行
4.3.5 更新阶段
- 当data里数据改变,更新DOM之前
- beforeUpdate-生命周期钩子函数被执行
- Virtual DOM......-虚拟DOM重新渲染,打补丁到真实DOM
- updated – 生命周期钩子函数被执行
- 当有data数据改变 – 重复这个循环
4.3.6 销毁阶段
- 当$destroy()被调用 – 比如组件DOM被移除(例v-if)
- beforeDestroy – 生命周期钩子函数被执行
- 拆卸数据监视器、子组件和事件侦听器
- 实例销毁后, 最后触发一个钩子
- destroyed – 生命周期钩子函数被执行
5 axios
5.1 axios的介绍
axios是一个专门用于发送ajax请求的库,一种前端异步请求后端的技术
原理
浏览器window接口的XMLHttpRequest
特点
- 支持客户端发送ajax请求
- 支持服务器端Node.js发送请求
- 支持Promise相关的语法
- 支持请求和响应的拦截器功能
- 自动转换JSON数据
axios的底层还是原生js实现, 内部通过Promise封装的,通用于前后端的请求库
5.2 axios使用
获取所有图书信息
5.3 get传参
在url?拼接 – 查询字符串
在url路径上 – 需要后端特殊处理
在请求体 / 请求头 传参给后台
5.4 post请求与传参
5.5 axios全局配置
配置基础地质,统一管理
axios.defaults.baseURL = "xxx"(当没配置的时候是主机地址)
修改请求url/以后的请求都不用带前缀基地址了 – 运行时, axios的baseURL会自动拼在前
6 $refs和$nextTick使用
6.1 获取DOM
通过id或ref属性获取原生DOM
在mounted生命周期 – 2种方式获取原生DOM标签
目标标签-添加id/ref
<h1 ref="myh1" id="h">1</h1>
恰当时机, 通过id / 通过ref属性 获取目标标签
6.2 获取组件对象
通过ref属性获取组件对象
创建Dome组件,写一个方法
app.vue使用Domo组件,给ref属性-名字随意
<组件 ref="myDome"></组件>
恰当时机,通过ref属性,获取组件对象,可调用组件对象里方法等(子传父的特殊方法,比较非主流,不常用)
6.3 $nextTick
点击该data,获取原生DOM内容
- 创建标签显示数据
- 点击+1,马上获取原生DOM内容
- 得到的结果仍是原来的count,因为vue检测数据更新,开启一个DOM更新队列(异步任务)
- 等DOM更新后,触发次方法里函数体执行:
- this.$nextTick(函数体)(一般情况下写的箭头函数,因为箭头函数没有this指向,使其this指向vue实例对象)
- nextTick的函数内部是封装了一个promise函数(他也会原地返回一个promise对象),其中执行的函数体代表.then
- nextTick是一个微任务,比起setTimeout的执行顺序要靠前(set是宏任务)
- vue中,每一次的数据更新,底层都对应一个微任务,用于更新DOM
6.4 组件name属性使用
组件name可用作注册组件名字
组件定义nam属性和值
注册组件可用上面name的值
可以对props: {}声明变量的值,可以进行效验
7 Vue组件-进阶
7.1 动态组件
多个组件使用同一个挂载点,并动态切换
方法: 设置挂载点<component>,使用is属性来设置要显示哪个组件
<component :is="要显示的动态组件名"></component>
7.2 组件缓存
频繁的切换组件,会导致组件频繁的创建和销毁
语法: vue内置的keep-alive组件把<component>包裹起来,可以进行组件缓存,防止次问题,提搞性能
可以缓存所有组件,不局限于<component>
7.3 组件缓存-激活的钩子函数
扩展两个新的生命周期函数
activated – 激活时触发
deactivated – 失去激活状态触发
配合组件缓存和组件动态挂载存在
7.4 组件插槽
通过slot标签, 让组件内可以接收不同的标签结构显示
语法口诀:
组件内,用<slot></slot>占位
使用组件时,传入标签替换slot标签(插入什么就在slot位置显示什么)
7.4.1 插槽默认内容
- 如果外面不给串,想给个默认显示内容
- 口诀: <slot>内放置内容,作为默认内容
- 不给组件传标签. slot内容显示
- 给组件内传标签, 则slot整体(包括默认)被换掉
7.4.2 具名插槽
组件内有2处以上不确定标签时,可以给slot起个名字
语法:
- slot使用name属性区分名字
- <slot name="content"></slot>
v-slot:可以简化成#
7.4.3 作用域插槽
- template配合v-slot:名字来分发对应的标签
- <template v-slot:content>需要分发标签</template>
使用插槽时,想要使用子组件内变量
口诀:
- 子组件, 在slot上绑定属性和子组件内的值
- 使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"
- scope变量名自动绑定slot上所有属性和值
- scope = {row: defaultObj}
- v-slot:name="scope"可以混用代表,作用域和名字
不能直接用另外一个组件里变量,所以需要用到作用域插槽,组件内标签可以随意定义和使用
作用域插槽子组件可以看做是一个局部作用域,其中通过自定义属性的方法,把其中变量赋值给自定义属性,使其可以被外部使用
7.5 自定义指令
获取标签,扩展额外的功能
全局注册-语法
Vue.directive("指令名", {
inserted(el) {
//可以对el标签扩展额外功能
}
})
局部注册-语法:
directives: {
"指令名字": {
inserted(el) {//对el进行操作}
}
}
// 使用v-指令名字激活指令
inserted方法----指令所在标签,被插入到网页中才会执行(一次)
update方法----当指令对应标签/数据发生更新时执行
通过自定义指令可以理解内置指令:例如v-if,v-for都可以看作是通过将标签作为参数传入到指令执行函数中,继而进行功能编写
7.5.1 自定义指令的传值
语法:
8 Vue路由系统
8.1 Vue路由简介和基础使用
(1) 映射: 相互对应的关系
(2) 路由: 映射关系
8.1.1 使用路由的原因
- 单页面应用(SPA): 所有功能在一个html页面上实现
- 前端路由作用: 实现业务场景切换
- 优点:
- 整体不刷新页面,用户体验更好
- 数据传递容易, 开发效率高
- 缺点:
- 开发成本高(需要学习专门知识)
- 首次加载会比较慢一点,不利于seo
8.1.2 vue-router
本质是一个第三方包
官网:Vue Router
vue-router模块包
- 它和 Vue.js 深度集成
- 可以定义 - 视图表(映射规则)
- 模块化的
- 提供2个内置全局组件
- 声明式导航自动激活的 CSS class 的链接
8.1.3 组件分类
.vue文件分2类,一个是页面组件,一个是复用组件
.vue文件本质无区别,方便大家学习和理解,总结的一个经验
src/views文件夹
页面组件-页面显示-配合路由用
src/components文件夹
复用组件-展示数据/常用于复用
8.1.4 vue-router模块
使用步骤:
- 下载vue-router模块到当前工程
- 在main.js中引入VueRouter函数
- 添加到Vue.use()身上-注册全局RouterLink和RouterView组件
- 创建路由规则数组-路径和组件名对应关系
- 用规则生成路由对象
- 把路由对象注入到new Vue实例中
- 用router-view作为挂载点, 切换不同的路由页面
@是src的绝对地址
8.2 声明式导航
8.2.1 声明式导航的基础使用
vue-router提供了一个全局组件 router-link
router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
router-link提供了声明式导航高亮的功能(自带类名)
当你点击 <router-link>
时,内部会调用这个方法,所以点击 <router-link :to="...">
相当于调用 router.push(...)
8.2.2 声明式导航-跳转传参
在router-link上的to属性传值,语法:
- /path?参数名=值
- /path/值 – 需要路由对象提前配置 path: “/path/参数名”
对应页面组件接收传递过来的值
- $route.query.参数名
- $route.params.参数名
8.3 路由重定向
匹配path后,强制跳转path路径
网页打开url默认hash值是/路径
redirect是设置要重定向到哪个路由路径 //浏览器url中#后的路径被改变成/find-重新匹配规则
8.3.1 路由-404配置
找不到路径给个提示页面
路由最后,path匹配*(任意路径)-前面不匹配就命中最后一个
8.3.2 路由-模式设置
修改路由在地址栏的模式
hash路由例如: http://localhost:8080/#/home
history路由例如: http://localhost:8080/home (以后上线需要服务器端支持, 否则找的是文件夹)
方式:
const router = new VueRouter({
routes,
mode: "history" //不写默认是hash路径
})
8.4 编程式导航
用JS代码来进行跳转
语法: path或者name任选一个
使用name时,需要给路由规则里添加name属性
name和path的使用场景:
无感知,易于扩展,方便修改用name属性(在页面上看不见随便定义)
path可以在url的hash值看到(尽量符合组内规范)
8.4.1 编程式导航-跳转传参
语法:
query和params任选一个
注意: 使用path会忽略params
推荐: name+query方式传参
注意: 如果当前url上"hash值和?参数"与你要跳转到的"hash值和?参数"一致,就会报出冗余导航的错误,不会跳转.
$touter.back()返回上一页
8.5 路由嵌套
(1)在现有的一级路由下,再嵌套二级路由
- src/views/Find.vue -- 发现音乐页
- src/views/My.vue -- 我的音乐页
- src/views/Second/Recommend.vue -- 发现音乐页 / 推荐页面
- src/views/Second/Ranking.vue -- 发现音乐页 / 排行榜页面
- src/views/Second/SongList.vue -- 发现音乐页 / 歌单页面
(2) main.js-继续配置2级路由
- 一级路由path从/开始定义
- 二级路由往后path直接写名字, 无需/开头(在一级路由规则里用children里书写)
- 嵌套路由在上级路由的children数组里编写路由信息对象
(3) 说明
- App.vue的router-view负责发现音乐和我的音乐页面, 切换
- Find.vue的的router-view负责发现音乐下的, 三个页面, 切换
(4) 注意
- 二级路由path一般不写根路径
- 跳转时路径要从/开始写全
- 二级路由如果加/表示根路径,即和父路由路径无关,只看自己写的路径
8.5.1 声明式导航-激活类名的区别
url上hash值(#/home/recommend)包含导航的href值(#/home) - 当前a就有 "router-link-active"类名
(#/home/recommend)和href(#/home/recommend)完全一致,添加router-link-exact-active类名
8.5.2 导航守卫
(1) 全局前置守卫
路由跳转之前,会触发一个函数
语法: router.beforeEach((to, from, next) => {}) //next(false)表示阻止跳转(当你要对路由权限判断时可以使用)
参数:
- to是一个路由的对象信息,表示要去往的路由页面
- from也是一个路由对象信息,表示从哪里跳转
- next一个函数体--next()才会让路由正常跳转,next(false)在原地停留,next("强制修改到另一个路由路径上")
- 注意:如果不调用next页面就留在原地
9 vant组件库
Vant是一个轻量、可靠的移动端 Vue 组件库, 开箱即用
Vant组件库: https://vant-contrib.gitee.io/vant/#/zh-CN/
参照文档即可
Vue.use表示执行模块的install方法,在Vue的内部挂载属性,方法或者注册全局组件
10 跨域解决方案--反向代理
本地node服务器开启cors,负责请求的转发和数据接收回传
跨域只是浏览器数据请求时会发生,服务器之间的请求不存在跨域问题
this.$route表示路由规则对象,当path发生变化时就转换对应path的对象,自定义的属性需要写在meta中,可根据需求填写
11 对于vue自定义事件$event的理解
在自定义事件中,子组件调用$emit,vue实例方法,来让父组件自定义的事件触发,在html页签上只需写@自定义事件名="事件处理函数"其中事件处理函数不传参的原因是,vue默认为事件处理函数传了一个参数$event,并在methods中通过形参来接收,所以也可以直接在html上写$event来接收子组件传来的数据,自定义事件与原生$event不同的是,它更多的是作为事件的传参,而不是事件对象
12 依赖注入
通过provide和inject实例对象可以实现父组件给所有子组件传值
在父组件中声明provide对象,以函数的形式返回一个对象中需要传递的数据,在子组件中使用inject接收(使用方式和props类似)
13 vuex模块化
vuex里的模块化属性:modules,相当于每一个小的vuex
访问方法:$store.state.模块名.state的属性名(可以通过vuex的getters属性返回此数据,以达到简写的目的)
修改数据的方法:通过模块内的mutations属性定义修改的函数,在未开启命名空间时,外部通过$commit("方法名", 实参);开启命名空间(namespaced:true),外部修改方式一:$commit("模块名/方法名", 实参)-----由于方法名中有/,所以只能用this.["模块名/方法名"]的方式来访问和调用,方式二:$commit("模块名", ["方法名"])-----this.方法名()即可实现访问和调用
14 Vue知识点补充
14.1 响应式
vue中不支持响应式的两种情况:1.直接通过数组下标来修改元素的内容 2.给对象追加新的属性并赋值
如果实际需求中必须要这样更改,通过this.$set(数组/对象,数组的索引/对象字段,具体的值)进行更改,这样就可以出发响应式了
14.2 vue组件的函数式
functional: true // 为true,表示该组件为一个函数式的组件(没有data,和响应式数据,只会接受props,没有this,它就是一个函数,在export default根对象中书写)
14.3 v-for的进阶用法
(1)循环元组
<li v-for="(movie, index) in [
'Lion King',
'Frozen',
'The Princess Bride'
]">
{{ index + 1 }} => {{ movie }}
</li>
(2)循环对象
<li v-for="(value, key) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
{{ key }}: {{ value }}
</li>
(3)循环对象(加上元组循环方式)
<li v-for="(value, key, index) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
{{ index + 1 }} => {{ key }}: {{ value }}
</li>
15 混入mixins
在vue组件中mixins属性可以将混入的obj对象中的属性和方法合并到vue组件中的存在的属性和方法(created,data...)中
格式为: mixins: [obj, obj2]
使用场景:在多个页面中使用同一个变量或方法时,可以定义混入组件,在main.js中引入
Vue.mixin({
methods: {
...具体方法
}
})