文章目录
- 一、 前端工程化
- webpack 的基本使用
- webpack 中的插件
- webpack 中的 loader
- 打包发布
- Source Map的使用
- 注意,实际开发不需要自己配置webpack
- 调试插件 vue-devtools
- 二、Vue基础
- 什么是 vue
- vue 的两个特性
- vue 指令
- 1. 内容渲染指令(v-text、插值表达式、v-html)
- 2. 属性绑定指令(v-bind 简写为:)
- 3. 事件绑定(v-on简写为@,事件修饰符、按键修饰符)
- 4. v-model 指令(双向绑定,重要)
- 5. 条件渲染指令(v-if(直接用这个) 和v-show)
- 6 列表渲染指令(v-for)
- 过滤器(vue3已淘汰,不作为重点)
- 监听器
- 计算属性
- axios(再加上async、await、promise),超重点!
- 三、vue-cli 的使用(重点)
- .vue文件初始化小技巧
- 安装和使用
- vue组件化开发(.vue文件)
- 组件化开发介绍
- props提高组件复用性
- 解决样式冲突问题
- 生命周期
- 组件之间的数据共享
- ref引用来获取dom
大家在学习vue知识之前,有要js、es6+、node.js等前置知识
这里给出大佬文章的传送门:
js、es6到11:
https://blog.csdn/qq_38490457/article/details/109257751?spm=1001.2014.3001.5506
node.js
https://blog.csdn/DDDHL_/article/details/124390573?spm=1001.2014.3001.5506
一、 前端工程化
1.小白眼中的前端开发 vs 实际的前端开发
小白眼中的前端开发:
- 会写 HTML + CSS + JavaScript 就会前端开发
- 需要美化页面样式,就拽一个 bootstrap 过来
- 需要操作 DOM 或发起 Ajax 请求,再拽一个 jQuery 过来
- 需要快速实现网页布局效果,就拽一个 Layui 过来
实际的前端开发:
- 模块化(js 的模块化、css 的模块化、资源的模块化)
- 组件化(复用现有的 UI 结构、样式、行为)
- 规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、 Git 分支管理)
- 自动化(自动化构建、自动部署、自动化测试)
-
什么是前端工程化:
前端工程化指的是:在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化、
标准化。
企业中的 Vue 项目和 React 项目,都是基于工程化的方式进行开发的。
好处:前端开发自成体系,有一套标准的开发方案和流程 -
前端工程化的解决方案
早期的前端工程化解决方案:
⚫ grunt( https://www.gruntjs/ )
⚫ gulp( https://www.gulpjs/ )
目前主流的前端工程化解决方案:
⚫ webpack( https://www.webpackjs/ )
⚫ parcel( https://zh.parceljs/ )
webpack 的基本使用
- 什么是 webpack
概念:webpack 是前端项目工程化的具体解决方案。
主要功能:它提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端 JavaScript 的兼容性、性
能优化等强大的功能。
好处:让程序员把工作的重心放到具体功能的实现上,提高了前端开发效率和项目的可维护性。
注意:目前 Vue,React 等前端项目,基本上都是基于 webpack 进行工程化开发的
注意:webpack用到了npm包管理工具,所以要先下载node.js
-
创建列表隔行变色项目
① 新建项目空白目录,并运行 npm init –y 命令,初始化包管理配置文件 package.json
② 新建 src 源代码目录
③ 新建 src -> index.html 首页和 src -> index.js 脚本文件
④ 初始化首页基本的结构
⑤ 运行 npm install jquery –S 命令,安装 jQuery
⑥ 通过 ES6 模块化的方式导入 jQuery,实现列表隔行变色效果 -
在项目中安装 webpack
在终端运行如下的命令,安装 webpack 相关的两个包:
npm install webpack@5.42.1 webpack-cli@4.7.2 -D
温馨提示:win命令行复制键 shift+insert
- 在项目中配置 webpack
实际中直接使用脚手架,不需要自己去配置
4.1 mode 的可选值
mode 节点的可选值有两个,分别是:
① development
⚫ 开发环境
⚫ 不会对打包生成的文件进行代码压缩和性能优化
⚫ 打包速度快,适合在开发阶段使用
② production
⚫ 生产环境
⚫ 会对打包生成的文件进行代码压缩和性能优化
⚫ 打包速度很慢,仅适合在项目发布阶段使用
4.2 webpack.config.js 文件的作用
webpack.config.js 是 webpack 的配置文件。webpack 在真正开始打包构建之前,会先读取这个配置文件,从而基于给定的配置,对项目进行打包。
注意:由于 webpack 是基于 node.js 开发出来的打包工具,因此在它的配置文件中,支持使用 node.js 相关的语法和模块进行 webpack 的个性化配置。
4.3 webpack 中的默认约定
在 webpack 4.x 和 5.x 的版本中,有如下的默认约定:
① 默认的打包入口文件为 src -> index.js
② 默认的输出文件路径为 dist -> main.js
注意:可以在 webpack.config.js 中修改打包的默认约定
4.4 自定义打包的入口与出口
在 webpack.config.js 配置文件中,通过 entry 节点指定打包的入口。通过 output 节点指定打包的出口。
示例代码如下
如图,现在webpack运行的是开发模式,指定处理Index1.js文件,生成的文件存放到dist文件夹的bundle.js文件中去
webpack 中的插件
- webpack 插件的作用
通过安装和配置第三方的插件,可以拓展 webpack 的能力,从而让 webpack 用起来更方便。最常用的
webpack 插件有如下两个:
① webpack-dev-server
⚫ 类似于 node.js 阶段用到的 nodemon 工具
⚫ 每当修改了源代码,webpack 会自动进行项目的打包和构建
② html-webpack-plugin
⚫ webpack 中的 HTML 插件(类似于一个模板引擎插件)
⚫ 可以通过此插件自定制 index.html 页面的内容
- webpack-dev-server
webpack-dev-server 可以让 webpack 监听项目源代码的变化,从而进行自动打包构建。当你每次保存代码的时候,这个插件就自动监测变化,自动给你打包构建,执行 npm run dev即可执行
想要退出监测就按两下CTRL+C
2.1 安装 webpack-dev-server
运行如下的命令,即可在项目中安装此插件:
npm install webpack-dev-server@3.11.2 -D
-D是把他下载到devDependencies中,即开发环境的依赖
-S是把他下载到dependencies中,即生产环境的依赖
2.2 配置 webpack-dev-server
访问浏览的80端口可以看到:
点击src,浏览器自动加载index.html
2.3 打包生成的文件哪儿去了?
① 不配置 webpack-dev-server 的情况下,webpack 打包生成的文件,会存放到实际的物理磁盘上
⚫ 严格遵守开发者在 webpack.config.js 中指定配置
⚫ 根据 output 节点指定路径进行存放
② 配置了 webpack-dev-server 之后,打包生成的文件存放到了内存中
⚫ 不再根据 output 节点指定的路径,存放到实际的物理磁盘上
⚫ 提高了实时打包输出的性能,因为内存比物理磁盘速度快很多
2.4 生成到内存中的文件该如何访问?
webpack-dev-server 生成到内存中的文件,默认放到了项目的根目录中,而且是虚拟的、不可见的。 ⚫ 可以直接用 / 表示项目根目录,后面跟上要访问的文件名称,即可访问内存中的文件
⚫ 例如 /bundle.js 就表示要访问 webpack-dev-server 生成到内存中的 bundle.js 文件
所以要加载内存中的bundle.js页面才会变化,因为更新的就是内存中的bundle.js
<script src="/bundle.js"></script>
- html-webpack-plugin
html-webpack-plugin 是 webpack 中的 HTML 插件,可以通过此插件自定制 index.html 页面的内容。
需求:我们通过80端口访问,没有直接显示index.html页面很难受,所以通过 html-webpack-plugin 插件,将 src 目录下的 index.html 首页,复制到项目根目录中一份
3.1 安装 html-webpack-plugin
运行如下的命令,即可在项目中安装此插件:
npm install html-webpack-plugin@5.3.2 -D
3.2 配置 html-webpack-plugin
配置webpack.config.js文件:
const path = require('path')
//1.导入html webpack plugin 插件,得到插件的构造函数
const HtmlPlugin = require('html-webpack-plugin')
// 2.创建html插件的实例对象
const htmlPlugin = new HtmlPlugin({
// 指定要复制哪个页面
template: './src/index.html',
filename: './index.html',
})
//使用node.js中的导出语法,向外导出一个webpack配置对象
module.exports={
// 代表webpack的运行模式,可选值有两个development和production
mode: 'development',
entry: path.join(__dirname,'./src/index1.js'),
output: {
path: path.join(__dirname,'dist'),
filename: 'bundle.js'
},
// 插件的数组,将来webpack在运行时,会加载并调用这些插件
plugins: [htmlPlugin],
}
3.3 解惑 html-webpack-plugin
① 通过 HTML 插件复制到项目根目录中的 index.html 页面,也被放到了内存中
② HTML 插件在生成的 index.html 页面,自动注入了打包的 bundle.js 文件
- devServer 节点
在 webpack.config.js 配置文件中,可以通过 devServer 节点对 webpack-dev-server 插件进行更多的配置,从而可以打包构建后在自动打开浏览器访问这个包/
示例代码如下:
注意:凡是修改了 webpack.config.js 配置文件,或修改了 package.json 配置文件,必须重启实时打包的服务器,否则最新的配置文件无法生效!
webpack 中的 loader
-
loader 概述
在实际开发过程中,webpack 默认只能打包处理以 .js 后缀名结尾的模块。其他非 .js 后缀名结尾的模块,webpack 默认处理不了,需要调用 loader 加载器才可以正常打包,否则会报错!
loader 加载器的作用:协助 webpack 打包处理特定的文件模块。比如:
⚫ css-loader 可以打包处理 .css 相关的文件
⚫ less-loader 可以打包处理 .less 相关的文件
⚫ babel-loader 可以打包处理 webpack 无法处理的高级 JS 语法 -
loader 的调用过程
-
打包处理 css 文件
① 运行 npm i style-loader@3.0.0 css-loader@5.2.6 -D 命令,安装处理 css 文件的 loader
② 在 webpack.config.js 的 module -> rules 数组中
其中,test 表示匹配的文件类型, use 表示对应要调用的 loader
注意:
⚫ use 数组中指定的 loader 顺序是固定的
⚫ 多个 loader 的调用顺序是:从后往前调用
4. 打包处理 less 文件
① 运行 npm i less-loader@10.0.1 less@4.1.1 -D 命令
② 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下
5. 打包处理样式表中与 url 路径相关的文件
① 运行 npm i url-loader@4.1.1 file-loader@6.2.0 -D 命令
② 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:
其中 ? 之后的是 loader 的参数项:
⚫ limit 用来指定图片的大小,单位是字节(byte)
⚫ 只有 ≤ limit 大小的图片,才会被转为 base64 格式的图片
- 打包处理 js 文件中的高级语法
webpack 只能打包处理一部分高级的 JavaScript 语法。对于那些 webpack 无法处理的高级 js 语法,需要借
助于 babel-loader 进行打包处理。例如 webpack 无法处理下面的 JavaScript 代码(装饰器):
6.1 安装 babel-loader 相关的包
运行如下的命令安装对应的依赖包:
npm i babel-loader@8.2.2 @babel/core@7.14.6 @babel/plugin-proposal-decorators@7.14.5 -D
在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:注意要排除第三方包,因为第三方包兼容性不用你解决,而且会影响性能。
6.2 配置 babel-loader
在项目根目录下,创建名为 babel.config.js 的配置文件,定义 Babel 的配置项如下:
打包发布
-
为什么要打包发布
项目开发完成之后,需要使用 webpack 对项目进行打包发布,主要原因有以下两点:
① 开发环境下,打包生成的文件存放于内存中,无法获取到最终打包生成的文件
② 开发环境下,打包生成的文件不会进行代码压缩和性能优化
为了让项目能够在生产环境中高性能的运行,因此需要对项目进行打包发布。 -
配置 webpack 的打包发布
在 package.json 文件的 scripts 节点下,新增 build 命令如下:–mode 来指定模式为production模式,覆盖之前的开发模式的
构建语句:
npm run build
-
把 JavaScript 文件统一生成到 js 目录中
在 webpack.config.js 配置文件的 output 节点中,进行如下的配置:
这样就会在dist中生成一个js文件夹,里面存放bundle.js文件 -
把图片文件统一生成到 image 目录中
修改 webpack.config.js 中的 url-loader 配置项,新增 outputPath 选项即可指定图片文件的输出路径:
-
自动清理 dist 目录下的旧文件
为了在每次打包发布时自动清理掉 dist 目录中的旧文件,可以安装并配置 clean-webpack-plugin 插件:
Source Map的使用
前端项目在投入生产环境之前,都需要对 JavaScript 源代码进行压缩混淆,从而减小文件的体积,提高文件的加载效率。此时就不可避免的产生了另一个问题:
对压缩混淆之后的代码除错(debug)是一件极其困难的事情
⚫ 变量被替换成没有任何语义的名称
⚫ 空行和注释被剔除
-
什么是 Source Map
Source Map 就是一个信息文件,里面储存着位置信息。也就是说,Source Map 文件中存储着压缩混淆后的代码,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大的方便后期的调试。
他的作用说白了就是调试的时候,浏览器debug报错显示的行和源代码的能对应上了,方便调试(要配置devtool)。 -
webpack 开发环境下的 Source Map
在开发环境下,webpack 默认启用了 Source Map 功能。当程序运行出错时,可以直接在控制台提示错误行
的位置,并定位到具体的源代码:
3.1 默认 Source Map 的问题
开发环境下默认生成的 Source Map,记录的是生成后的代码的位置。会导致运行时报错的行数与源代码的行
数不一致的问题。示意图如下:
3.2 解决默认 Source Map 的问题
开发环境下,推荐在 webpack.config.js 中添加如下的配置,即可保证运行时报错的行数与源代码的行数
保持一致:即devtool 设置为 eval-source-map,然后这两个行号就一致了。
4. webpack 生产环境下的 Source Map
在生产环境下,如果省略了 devtool 选项,则最终生成的文件中不包含 Source Map。这能够防止原始代码通 过 Source Map 的形式暴露给别有所图之人。
4.1 只定位行数不暴露源码
在生产环境下,如果只想定位报错的具体行数,且不想暴露源码。此时可以将 devtool 的值设置为
nosources-source-map。实际效果如图所示:
4.2 定位行数且暴露源码(强烈不建议)
在生产环境下,如果想在定位报错行数的同时,展示具体报错的源码。此时可以将 devtool 的值设置为
source-map。实际效果如图所示:
5. Source Map 的最佳实践
① 开发环境下:
⚫ 建议把 devtool 的值设置为 eval-source-map
⚫ 好处:可以精准定位到具体的错误行
② 生产环境下:
⚫ 建议关闭 Source Map 或将 devtool 的值设置为 nosources-source-map
⚫ 好处:防止源码泄露,提高网站的安全性
注意,实际开发不需要自己配置webpack
实际开发会使用脚手架CLI一键生成webpack项目,上面的知识都是webpack的配置原理,理解即可,这个有点像ssm和springboot的地位,会了ssm配置,实际也用不上,springboot帮你快速构建了,开箱即用。
总结:
① 能够掌握 webpack 的基本使用
⚫ 安装、webpack.config.js、修改打包入口
② 了解常用的 plugin 的基本使用
⚫ webpack-dev-server、html-webpack-plugin
③ 了解常用的 loader 的基本使用
⚫ loader 的作用、loader 的调用过程
④ 能够说出 Source Map 的作用
⚫ 精准定位到错误行并显示对应的源码
⚫ 方便开发者调试源码中的错误
调试插件 vue-devtools
在谷歌浏览器中安装该插件,要点开开发者模式然后拖动插件才行,但是我失败了,直接解压添加。
添加完后,点击右键检查,检查页面中会有vue窗口:
二、Vue基础
什么是 vue
- 构建用户界面
- 用 vue 往 html 页面中填充数据,非常的方便
- 框架
- 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
- 要学习 vue,就是在学习 vue 框架中规定的用法!
- vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
- 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!
vue 的两个特性
-
数据驱动视图:
- 数据的变化会驱动视图自动更新
- 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
-
双向数据绑定:
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
- js 数据的变化,会被自动渲染到页面上
- 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中
注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)
vue 指令
1. 内容渲染指令(v-text、插值表达式、v-html)
v-text
指令的缺点:会覆盖元素内部原有的内容!比如下面的女会把性别给覆盖,所以它实际开发用的不多。{{ }}
插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!v-html
指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<p v-text="username"></p>
<p v-text="gender">性别:</p>
<hr>
<p>姓名:{{ username }}</p>
<p>性别:{{ gender }}</p>
<hr>
<div v-text="info"></div>
<div>{{ info }}</div>
<div v-html="info"></div>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
username: 'zhangsan',
gender: '女',
info: '<h4 style="color: red; font-weight: bold;">欢迎大家来学习 vue.js</h4>'
}
})
</script>
</body>
</html>
界面详情:
注意:el挂载最好挂载最大的div,不要绑定其中的某个标签或指令等,例如绑定p标签只有第一个p标签生效。
2. 属性绑定指令(v-bind 简写为:)
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
-
在 vue 中,可以使用
v-bind:
指令,为元素的属性动态绑定值; -
简写是英文的
:
-
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<div :title="'box' + index">这是一个 div</div>
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<input type="text" :placeholder="tips">
<hr>
<!-- vue 规定 v-bind: 指令可以简写为 : -->
<img :src="photo" alt="" style="width: 150px;">
<hr>
<div>1 + 2 的结果是:{{ 1 + 2 }}</div>
<div>{{ tips }} 反转的结果是:{{ tips.split('').reverse().join('') }}</div>
<div :title="'box' + index">这是一个 div</div>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
tips: '请输入用户名',
photo: 'https://cn.vuejs/images/logo.svg',
index: 3
}
})
</script>
</body>
</html>
效果:
3. 事件绑定(v-on简写为@,事件修饰符、按键修饰符)
-
v-on:
简写是@
-
语法格式为:
<button @click="add"></button> methods: { add() { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<p>count 的值是:{{ count }}</p>
<!-- 在绑定事件处理函数的时候,可以使用 () 传递参数 -->
<!-- v-on: 指令可以被简写为 @ -->
<button @click="add(1)">+1</button>
<button @click="sub">-1</button>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
count: 0
},
// methods 的作用,就是定义事件的处理函数
methods: {
add(n) {
// 在 methods 处理函数中,this 就是 new 出来的 vm 实例对象
// console.log(vm === this)
console.log(vm)
// vm.count += 1
this.count += n
},
sub() {
// console.log('触发了 sub 处理函数')
this.count -= 1
}
}
})
</script>
</body>
</html>
效果:
-
$event
的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event(因为如果没有参数是有事件对象e的,但是没参数就没有了)。例如:<button @click="add(3, $event)"></button> methods: { add(n, e) { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }
-
事件修饰符:
-
.prevent
阻止默认行为(如a链接的跳转)<a @click.prevent="xxx">链接</a>
-
.stop
(阻止事件冒泡,即DOM中子元素的事件传递到父元素发生)<button @click.stop="xxx">按钮</button>
-
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<a href="http://www.baidu" @click.prevent="show">跳转到百度首页</a>
<hr>
<div style="height: 150px; background-color: orange; padding-left: 100px; line-height: 150px;" @click="divHandler">
<button @click.stop="btnHandler">按钮</button>
</div>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {},
methods: {
show() {
console.log('点击了 a 链接')
},
btnHandler() {
console.log('btnHandler')
},
divHandler() {
console.log('divHandler')
}
},
})
</script>
</body>
</html>
页面效果:
5.按键修饰符
在监听键盘事件时,我们经常需要判断详细的按键。此时,可以为键盘相关的事件添加按键修饰符,例如:
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<input type="text" @keyup.esc="clearInput" @keyup.enter="commitAjax">
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {},
methods: {
clearInput(e) {
console.log('触发了 clearInput 方法')
e.target.value = ''
},
commitAjax() {
console.log('触发了 commitAjax 方法')
}
},
})
</script>
</body>
</html>
效果:
4. v-model 指令(双向绑定,重要)
vue 提供了 v-model 双向数据绑定指令,用来辅助开发者在不操作 DOM 的前提下,快速获取表单的数据。
应用场景:
- input 输入框
- type=“radio”
- type=“checkbox”
- type=“xxxx”
- textarea
- select
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们在把数据填充到 div 内部 -->
<div id="app">
<p>用户的名字是:{{ username }}</p>
<input type="text" v-model="username">
<hr>
<input type="text" :value="username">
<hr>
<select v-model="city">
<option value="">请选择城市</option>
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">广州</option>
</select>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
username: 'zhangsan',
city: '2'
}
})
</script>
</body>
</html>
效果:输入框的值修改会修改data的值
v-model 指令的修饰符:
为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符,分别是:
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转为数值类型 | <input v-model.number=“age” /> |
.trim | 自动过滤用户输入的首尾空白字符 | <input v-model.trim=“msg” /> |
.lazy | 在“change”时而非“input”时更新 | <input v-model.lazy=“msg” /> |
为什么要使用.number,因为用户一输入,number类型就变为string了,就无法完成运算了
代码案例:不展示效果了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们把数据填充到 div 内部 -->
<div id="app">
<input type="text" v-model.number="n1"> + <input type="text" v-model.number="n2"> = <span>{{ n1 + n2 }}</span>
<hr>
<input type="text" v-model.trim="username">
<button @click="showName">获取用户名</button>
<hr>
<input type="text" v-model.lazy="username">
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
username: 'zhangsan',
n1: 1,
n2: 2
},
methods: {
showName() {
console.log(`用户名是:"${this.username}"`)
}
},
})
</script>
</body>
</html>
5. 条件渲染指令(v-if(直接用这个) 和v-show)
v-show
的原理是:动态为元素添加- 或移除
display: none
样式,来实现元素的显示和隐藏- 如果要频繁的切换元素的显示状态,用 v-show 性能会更好
v-if
的原理是:每次动态创建或移除元素,实现元素的显示和隐藏- 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好(因为此时不会被创建,没有dom操作,而v-show一定会被创建,只是后来会改变样式)
在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!
v-if 指令在使用的时候,有两种方式:还有if else 和 else等指令,具体看示例代码
-
直接给定一个布尔值 true 或 false
<p v-if="true">被 v-if 控制的元素</p>
-
给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏
<p v-if="type === 'A'">良好</p>
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们把数据填充到 div 内部 -->
<div id="app">
<p v-if="flag">这是被 v-if 控制的元素</p>
<p v-show="flag">这是被 v-show 控制的元素</p>
<hr>
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
// 如果 flag 为 true,则显示被控制的元素;如果为 false 则隐藏被控制的元素
flag: false,
type: 'A'
}
})
</script>
</body>
</html>
效果:
6 列表渲染指令(v-for)
vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。v-for 指令需要使
用 item in items 形式的特殊语法,其中:
⚫ items 是待循环的数组
⚫ item 是被循环的每一项
v-for 中的索引:
v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为 (item, index) in items
代码示例:直接用.语法使用属性,注意v-for一定要加key
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./lib/bootstrap.css">
</head>
<body>
<!-- 希望 Vue 能够控制下面的这个 div,帮我们把数据填充到 div 内部 -->
<div id="app">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th>
<th>Id</th>
<th>姓名</th>
</thead>
<tbody>
<!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 -->
<!-- 而且,尽量把 id 作为 key 的值 -->
<!-- 官方对 key 的值类型,是有要求的:字符串或数字类型 -->
<!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ index }}</td>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 1. 导入 Vue 的库文件,在 window 全局就有了 Vue 这个构造函数 -->
<script src="./lib/vue-2.6.12.js"></script>
<!-- 2. 创建 Vue 的实例对象 -->
<script>
// 创建 Vue 的实例对象
const vm = new Vue({
// el 属性是固定的写法,表示当前 vm 实例要控制页面上的哪个区域,接收的值是一个选择器
el: '#app',
// data 对象就是要渲染到页面上的数据
data: {
list: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' },
{ id: 4, name: '张三' },
]
}
})
</script>
</body>
</html>
效果:
使用 key 维护列表的状态:
当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种
默认的性能优化策略,会导致有状态的列表无法被正确更新。
为了给 vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲
染的性能。此时,需要为每项提供一个唯一的 key 属性:
key 的注意事项
① key 的值只能是字符串或数字类型
② key 的值必须具有唯一性(即:key 的值不能重复)
③ 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
④ 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
⑤ 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)
过滤器(vue3已淘汰,不作为重点)
定义过滤器:
在创建 vue 实例期间,可以在 filters 节点中定义过滤器,示例代码如下:
私有过滤器和全局过滤器:
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
如果希望在多个 vue 实例之间共享过滤器,则可以按照如下的格式定义全局过滤器:
连续调用多个过滤器
过滤器可以串联地进行调用,例如:
过滤器传参:
过滤器的本质是 JavaScript 函数,因此可以接收参数,格式如下:
过滤器的兼容性:
过滤器仅在 vue 2.x 和 1.x 中受支持,在 vue 3.x 的版本中剔除了过滤器相关的功能。
在企业级项目开发中:
⚫ 如果使用的是 2.x 版本的 vue,则依然可以使用过滤器相关的功能
⚫ 如果项目已经升级到了 3.x 版本的 vue,官方建议使用计算属性或方法代替被剔除的过滤器功能
具体的迁移指南,请参考 vue 3.x 的官方文档给出的说明:
https://v3.vuejs/guide/migration/filters.html#migration-strategy
监听器
- 什么是 watch 侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
语法格式如下:
- 使用 watch 检测用户名是否可用
- immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使
用 immediate 选项。示例代码如下:
- deep 选项
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项,代码示例如下:
- 监听对象单个属性的变化
计算属性
- 什么是计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或 methods 方法使用。示例代码如下
- 计算属性的特点
① 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
② 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./lib/vue-2.6.12.js"></script>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<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 盒子 -->
<!-- 在属性身上,: 代表 v-bind: 属性绑定 -->
<!-- :style 代表动态绑定一个样式对象,它的值是一个 { } 样式对象 -->
<!-- 当前的样式对象中,只包含 backgroundColor 背景颜色 -->
<div class="box" :style="{ backgroundColor: rgb }">
{{ rgb }}
<!-- 原来没用计算属性是: -->
<!-- <div class="box" :style="{ backgroundColor: `rgb(${r}, ${g}, ${b})` }"> -->
<!-- {{ `rgb(${r}, ${g}, ${b})` }} -->
</div>
<button @click="show">按钮</button>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
// 红色
r: 0,
// 绿色
g: 0,
// 蓝色
b: 0
},
methods: {
// 点击按钮,在终端显示最新的颜色
show() {
console.log(this.rgb)
}
},
// 所有的计算属性,都要定义到 computed 节点之下
// 计算属性在定义的时候,要定义成“方法格式”
computed: {
// rgb 作为一个计算属性,被定义成了方法格式,
// 最终,在这个方法中,要返回一个生成好的 rgb(x,x,x) 的字符串
rgb() {
return `rgb(${this.r}, ${this.g}, ${this.b})`
}
}
});
console.log(vm)
</script>
</body>
</html>
效果:
axios(再加上async、await、promise),超重点!
axios 是一个专注于网络请求的库!
常见搭配:axios函数被await修饰,外面的函数被async修饰:
axios函数的返回值是一个promise类型的,用await可以取出其中的值,然后必须在async函数中。
promise是为了解决回调地狱在es6中出现的,async和awit是语法糖。
axios 的基本使用:
-
发起 GET 请求:
axios({ // 请求方式 method: 'GET', // 请求的地址 url: 'http://www.liulongbin.top:3006/api/getbooks', // URL 中的查询参数 params: { id: 1 } }).then(function (result) { console.log(result) })
-
发起 POST 请求:
document.querySelector('#btnPost').addEventListener('click', async function () { // 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await! // await 只能用在被 async “修饰”的方法中 const { data: res } = await axios({ method: 'POST', url: 'http://www.liulongbin.top:3006/api/post', data: { name: 'zs', age: 20 } }) console.log(res) })
基本使用代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./lib/axios.js"></script>
<script>
// http://www.liulongbin.top:3006/api/getbooks
// 1. 调用 axios 方法得到的返回值是 Promise 对象
axios({
// 请求方式
method: 'GET',
// 请求的地址
url: 'http://www.liulongbin.top:3006/api/getbooks',
// URL 中的查询参数
params: {
id: 1
},
// 请求体参数
data: {}
}).then(function (result) {
console.log(result)
})
</script>
</body>
</html>
有个很重要的一点,axios请求得到的数据有好几个:其中只有data里的数据是后端接口给我们的,其他是axios给我们封装的,因此取数据我们要提取一下
代码示例:把结果的data解构出来,并重命名为res,因为data.data太不舒服
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btnPost">发起POST请求</button>
<button id="btnGet">发起GET请求</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnPost').addEventListener('click', async function () {
// 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
// await 只能用在被 async “修饰”的方法中
const { data } = await axios({
method: 'POST',
url: 'http://www.liulongbin.top:3006/api/post',
data: {
name: 'zs',
age: 20
}
})
console.log(data)
})
document.querySelector('#btnGet').addEventListener('click', async function () {
// 解构赋值的时候,使用 : 进行重命名
// 1. 调用 axios 之后,使用 async/await 进行简化
// 2. 使用解构赋值,从 axios 封装的大对象中,把 data 属性解构出来
// 3. 把解构出来的 data 属性,使用 冒号 进行重命名,一般都重命名为 { data: res }
const { data: res } = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
console.log(res.data)
})
// $.ajax() $.get() $.post()
// axios() axios.get() axios.post() axios.delete() axios.put()
</script>
</body>
</html>
跟ajax一样,axios也有get和post两种快捷使用方式:
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btnGET">GET</button>
<button id="btnPOST">POST</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGET').addEventListener('click', async function () {
/* axios.get('url地址', {
// GET 参数
params: {}
}) */
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: { id: 1 }
})
console.log(res)
})
document.querySelector('#btnPOST').addEventListener('click', async function () {
// axios.post('url', { /* POST 请求体数据 */ })
const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
console.log(res)
})
</script>
</body>
</html>
三、vue-cli 的使用(重点)
.vue文件初始化小技巧
在创建一个vue文件,什么都没有时,输入一个 < 符号选择这一项:
vue的模板将自动创建,template+script+style
1.什么是单页面应用程序
单页面应用程序(英文名:Single Page Application)简称 SPA,顾名
思义,指的是一个 Web 网站中只有唯一的一个 HTML 页面,所有的功能
与交互都在这唯一的一个页面内完成。
例如黑马的这个项目,跳转来跳转去只是在一个html元素中进行的,故称为单页面应用,是使用了index.html元素的一个div来实现的,然后各种vue文件以组件的形式对这个页面进行渲染:
2.什么是vue-cli
如果把webpack比作前端版的maven的话,vue-cli我觉得像后端中的springboot框架,可以快速构建一个项目,开箱即用。
vue-cli 是 Vue.js 开发的标准工具。它简化了程序员基于 webpack 创建工程化的 Vue 项目的过程。
引用自 vue-cli 官网上的一句话:
程序员可以专注在撰写应用上,而不必花好几天去纠结 webpack 配置的问题。
中文官网:https://cli.vuejs/zh/
安装和使用
vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上:
npm install -g @vue/cli
基于 vue-cli 快速生成工程化的 Vue 项目:
vue create 项目的名称
-
在终端下运行如下的命令,创建指定名称的项目:
vue cerate 项目的名称
2. 创建目录后,下一步选择预设:
用方向键选择,第一个第二个是现成的方案,支持vue2,和vue3,选后就可自动构建,第三个是自己选择配置,这里由于是初学者所以自己配置。
下一步选择:选择Babel用来解析高级js代码,选择css预处理器来可以使用less,这里不建议选Linter/Formatter,这是规范代码风格的,选了后会频繁报错。
然后选择vue的版本,这里选择vue2
css预处理器,这里选择less
然后会问你,babel等第三方插件配置文件,是独立放置还是和package.json放在一起,这里选择独立:
然后问你要不要把刚才的选择存起来,当作预设下次用,并让你输入预设名字
接下来就是自动创建项目了,这个时候鼠标最好不要乱动,以免暂停窗口,可以用ctrl C恢复
使用npm run serve来执行,执行成功后从浏览器url访问
vue 项目的运行流程
在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
其中:
① App.vue 用来编写待渲染的模板结构
② index.html 中需要预留一个 el 区域
③ main.js 把 App.vue 渲染到了 index.html 所预留的区域中
一个单页面index.html,组件们都挂载到app这个div上进行渲染
main.js把App.vue渲染到html页面上,在这里实例化vue对象,然后通过render函数把Test组件渲染到html页面,这里$mount相当于原来的el挂载 app
这是test.vue的内容
<template>
<div>
<div class="test-box">
<h3>这是用户自定义的 Test.vue --- {{ username }}</h3>
<button @click="chagneName">修改用户名</button>
</div>
<div>123</div>
</div>
</template>
<script>
// 默认导出。这是固定写法!
export default {
// data 数据源
// 注意:.vue 组件中的 data 不能像之前一样,不能指向对象。
// 注意:组件中的 data 必须是一个函数
data() {
// 这个 return 出去的 { } 中,可以定义数据
return {
username: 'admin'
}
},
methods: {
chagneName() {
// 在组件中, this 就表示当前组件的实例对象
console.log(this)
this.username = '哇哈哈'
}
},
// 当前组件中的侦听器
watch: {},
// 当前组件中的计算属性
computed: {},
// 当前组件中的过滤器
filters: {}
}
</script>
<style lang="less">
.test-box {
background-color: pink;
h3 {
color: red;
}
}
</style>
vue 项目中 src 目录的构成:
assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
main.js 是项目的入口文件。整个项目的运行,要先执行 main.js
App.vue 是项目的根组件。
vue组件化开发(.vue文件)
组件化开发介绍
-
什么是组件化开发
组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。 -
vue 中的组件化开发
vue 是一个支持组件化开发的前端框架。
vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。 -
vue 组件的三个组成部分
每个 .vue 组件都由 3 部分构成,分别是:
template -> 组件的模板结构
script -> 组件的 JavaScript 行为
style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
3.1 template
vue 规定:每个组件对应的模板结构,需要定义到 节点中。负责提供组件的ui结构
注意:
- template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素
- template 中只能包含唯一的根节点(即只能写一个div,里面可以包含小的,但是最大的只有一个)
3.2 script
vue 规定:开发者可以在 <script> 节点中封装组件的 JavaScript 业务逻辑。
<script > 节点的基本结构如下:
data和methods等结点,都要写在export default里面,这是一种固定写法
.vue 组件中的 data 必须是函数:
vue 规定:.vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。
因此在组件中定义 data 数据节点时,data一定要定义为一个函数,并有return语句:其他的结点和之前的一样
3.3 style
vue 规定:组件内的 <style> 节点是可选的,开发者可以在<style> 节点中编写样式美化当前组件的 UI 结构。
在<style> 标签上添加 lang=“less” 属性,即可使用 less 语法编写组件的样式:
4. 组件之间的父子关系
4.1 使用组件的三个步骤
App.vue使用其他两个组件作为子组件:
<template>
<div class="app-container">
<h1>App 根组件</h1>
<button @click="flag = !flag">Toggle Flag</button>
<Test info="你好" v-if="flag"></Test>
<hr />
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 3. 以标签形式,使用注册好的组件 -->
<Left></Left>
<Right></Right>
</div>
</div>
</template>
<script>
// 1. 导入需要使用的 .vue 组件
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'
import Test from '@/components/Test.vue'
export default {
data() {
return {
flag: true
}
},
// 2. 注册组件
components: {
Left,
Right,
Test
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
4.2 通过 components 注册的是私有子组件
例如:
在组件 A 的 components 节点下,注册了组件 F。
则组件 F 只能用在组件 A 中;不能被用在组件 C 中。
请大家思考两个问题:
① 为什么 F 不能用在组件 C 中?
② 怎样才能在组件 C 中使用 F?
4.3 注册全局组件
在 vue 项目的 main.js 入口文件中,通过 Vueponent() 方法,可以注册全局组件。示例代码如下:
使用的时候,就可以直接使用 MyCount标签了
props提高组件复用性
- 组件的 props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性!,比如我想将组件中的属性定义为自己想要的初值,就可以用自定义属性定义。
它的语法格式如下:
代码示例:两个组件的init各种有各自的初始值
用v-bind来绑定init属性:
<MyCount :init=“9”>
让9变成了数值9,而不是字符串9,变成了js代码。
5.1 props 是只读的
可以用this把props的值交给data里的元素,然后用data里的进行操作。
5.2 props 的 default 默认值
在声明自定义属性时,如果外界组件没有传递init属性,可以通过 default 来定义属性的默认值。在用户没有传init时会使用,传了的话会覆盖默认值,示例代码如下:
不能再指向数组了,要指向对象
5.3 props 的 type 值类型
在声明自定义属性时,可以通过 type 来定义属性的值类型。示例代码如下:
规定传过来的init值类型必须是值类型,要不会报错
5.4 props 的 required 必填项
在声明自定义属性时,可以通过 required 选项,将属性设置为必填项,强制用户必须传递属性的值。示例代码如下:所引用得组件必须得传一个init,有默认值也没用,否则在终端报错。
解决样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
① 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
② 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
6.1 思考:如何解决组件样式冲突的问题
为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域,示例代码如下:
都加一个属性:
然后写样式时,指定属性就行,但是有简便操作,在style标签加上scoped属性,vue会自动为不同的标签加上data-v,同一个组件加的data-v是一样的:
自动生成一样的data-v…
6.3 /deep/ 样式穿透
如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样
式对子组件生效,可以使用 /deep/ 深度选择器。
生命周期
我们的 .vue组件是如何工作的呢,是变成标签了吗,不,是被编译成js文件了。然后加载到html页面上了。
当使用标签的释放使用别的组件的时候,就相当于创建了一个组件的实例。组件的创建是有一个生命周期的。
- 生命周期 & 生命周期函数
生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。
生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
生命周期函数就是在组件创建的某个阶段你去进行什么操作:
4. 生命周期图示
可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://cn.vuejs/v2/guide/instance.html#生命周期图示
就是这个图:
created方法最常用,用于使用ajx请求来获得数据,但是beforeCreate不行,因为此时还没有加载data和method
<template>
<div class="test-container">
<h3 id="myh3">Test.vue 组件 --- {{ books.length }} 本图书</h3>
<p id="pppp">message 的值是:{{ message }}</p>
<button @click="message += '~'">修改 message 的值</button>
</div>
</template>
<script>
export default {
props: ['info'],
data() {
return {
message: 'hello vue.js',
// 定义 books 数组,存储的是所有图书的列表数据。默认为空数组!
books: []
}
},
watch: {
message(newVal) {
console.log('监视到了 message 的变化:' + newVal)
}
},
methods: {
show() {
console.log('调用了 Test 组件的 show 方法!')
},
// 使用 Ajax 请求图书列表的数据
initBookList() {
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', () => {
const result = JSON.parse(xhr.responseText)
console.log(result)
this.books = result.data
})
xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks')
xhr.send()
}
},
// 创建阶段的第1个生命周期函数
beforeCreate() {
// console.log(this.info)
// console.log(this.message)
// this.show()
},
created() {
// created 生命周期函数,非常常用。
// 经常在它里面,调用 methods 中的方法,请求服务器的数据。
// 并且,把请求到的数据,转存到 data 中,供 template 模板渲染的时候使用!
this.initBookList()
},
beforeMount() {
// 这个不怎么用,因为还不能操作dom元素
// console.log('beforeMount')
// const dom = document.querySelector('#myh3')
// console.log(dom)
},
// 如果要操作当前组件的 DOM,最早,只能在 mounted 阶段执行,这个阶段时组件创建阶段已经结束
mounted() {
// console.log(this.$el)
// const dom = document.querySelector('#myh3')
// console.log(dom)
},
beforeUpdate() {
// console.log('beforeUpdate')
// console.log(this.message)
// const dom = document.querySelector('#pppp')
// console.log(dom.innerHTML)
},
// 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
updated() {
// console.log('updated')
// console.log(this.message)
// const dom = document.querySelector('#pppp')
// console.log(dom.innerHTML)
},
beforeDestroy() {
console.log('beforeDestroy')
this.message = 'aaa'
console.log(this.message)
},
destroyed() {
console.log('destroyed')
// this.message = 'aaa'
}
}
</script>
<style lang="less" scoped>
.test-container {
background-color: pink;
height: 200px;
}
</style>
组件之间的数据共享
-
组件之间的关系
在项目开发中,组件之间的最常见的关系分为如下两种: ① 父子关系
② 兄弟关系 -
父子组件之间的数据共享
父子组件之间的数据共享又分为:
① 父 -> 子共享数据
② 子 -> 父共享数据
演示:父向子传值 请注意props是只读的,请不要改变其值
2.2 子组件向父组件共享数据
子组件向父组件共享数据使用自定义事件。使用$emit函数,触发一个自定义事件并传递一个数据,示例代码如下:
3. 兄弟组件之间的数据共享
在 vue2.x 中,兄弟组件之间数据共享的方案是 EventBus。
然后一个用
e
m
i
t
发送数据,另一个用
emit发送数据,另一个用
emit发送数据,另一个用on接受数据
EventBus 的使用步骤
① 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
② 在数据发送方,调用 bus.
e
m
i
t
(
′
事件名
称
′
,
要发送的数据
)
方法触发自定义事件③在数据接收方,调用
b
u
s
.
emit('事件名称', 要发送的数据) 方法触发自定义事件 ③ 在数据接收方,调用 bus.
emit(′事件名称′,要发送的数据)方法触发自定义事件③在数据接收方,调用bus.on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件
ref引用来获取dom
- 什么是 ref 引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象(内置对象),里面存储着对应的 DOM 元素或组件的引用。默认情况下,
组件的 $refs 指向一个空对象。
- 使用 ref 引用 DOM 元素
如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作
- 使用 ref 引用组件实例
如果想要使用 ref 引用页面上的组件实例,则可以按照如下的方式进行操作:
- 控制文本框和按钮的按需切换
- 让文本框自动获得焦点
- this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。要不然组件还没更新,文本框dom还没有,你怎么去操作呢。
更多推荐
vue全套笔记,前端工程化+vue指令+脚手架
发布评论