vue全套笔记,前端工程化+vue指令+脚手架


大家在学习vue知识之前,有要js、es6+、node.js等前置知识
这里给出大佬文章的传送门:

js、es6到11:

https://blog.csdn.net/qq_38490457/article/details/109257751?spm=1001.2014.3001.5506

node.js

https://blog.csdn.net/DDDHL_/article/details/124390573?spm=1001.2014.3001.5506

一、 前端工程化

1.小白眼中的前端开发 vs 实际的前端开发
小白眼中的前端开发:

  • 会写 HTML + CSS + JavaScript 就会前端开发
  • 需要美化页面样式,就拽一个 bootstrap 过来
  • 需要操作 DOM 或发起 Ajax 请求,再拽一个 jQuery 过来
  • 需要快速实现网页布局效果,就拽一个 Layui 过来

实际的前端开发:

  • 模块化(js 的模块化、css 的模块化、资源的模块化)
  • 组件化(复用现有的 UI 结构、样式、行为)
  • 规范化(目录结构的划分、编码规范化、接口规范化、文档规范化、 Git 分支管理)
  • 自动化(自动化构建、自动部署、自动化测试)
  1. 什么是前端工程化:
    前端工程化指的是:在企业级的前端项目开发中,把前端开发所需的工具、技术、流程、经验等进行规范化、
    标准化。
    企业中的 Vue 项目和 React 项目,都是基于工程化的方式进行开发的。
    好处:前端开发自成体系,有一套标准的开发方案和流程

  2. 前端工程化的解决方案
    早期的前端工程化解决方案:
    ⚫ grunt( https://www.gruntjs.net/ )
    ⚫ gulp( https://www.gulpjs.com.cn/ )
    目前主流的前端工程化解决方案:
    ⚫ webpack( https://www.webpackjs.com/ )
    ⚫ parcel( https://zh.parceljs.org/ )

webpack 的基本使用

  1. 什么是 webpack
    概念:webpack 是前端项目工程化的具体解决方案。
    主要功能:它提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端 JavaScript 的兼容性、性
    能优化等强大的功能。
    好处:让程序员把工作的重心放到具体功能的实现上,提高了前端开发效率和项目的可维护性。
    注意:目前 Vue,React 等前端项目,基本上都是基于 webpack 进行工程化开发的

注意:webpack用到了npm包管理工具,所以要先下载node.js

  1. 创建列表隔行变色项目
    ① 新建项目空白目录,并运行 npm init –y 命令,初始化包管理配置文件 package.json
    ② 新建 src 源代码目录
    ③ 新建 src -> index.html 首页和 src -> index.js 脚本文件
    ④ 初始化首页基本的结构
    ⑤ 运行 npm install jquery –S 命令,安装 jQuery
    ⑥ 通过 ES6 模块化的方式导入 jQuery,实现列表隔行变色效果

  2. 在项目中安装 webpack
    在终端运行如下的命令,安装 webpack 相关的两个包:

npm install webpack@5.42.1 webpack-cli@4.7.2 -D

温馨提示:win命令行复制键 shift+insert

  1. 在项目中配置 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 中的插件

  1. webpack 插件的作用

通过安装和配置第三方的插件,可以拓展 webpack 的能力,从而让 webpack 用起来更方便。最常用的
webpack 插件有如下两个:
① webpack-dev-server
⚫ 类似于 node.js 阶段用到的 nodemon 工具
⚫ 每当修改了源代码,webpack 会自动进行项目的打包和构建
② html-webpack-plugin
⚫ webpack 中的 HTML 插件(类似于一个模板引擎插件)
⚫ 可以通过此插件自定制 index.html 页面的内容

  1. 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>
  1. 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 文件

  1. devServer 节点
    在 webpack.config.js 配置文件中,可以通过 devServer 节点对 webpack-dev-server 插件进行更多的配置,从而可以打包构建后在自动打开浏览器访问这个包/
    示例代码如下:
    注意:凡是修改了 webpack.config.js 配置文件,或修改了 package.json 配置文件,必须重启实时打包的服务器,否则最新的配置文件无法生效!
    在这里插入图片描述

webpack 中的 loader

  1. loader 概述
    在实际开发过程中,webpack 默认只能打包处理以 .js 后缀名结尾的模块。其他非 .js 后缀名结尾的模块,webpack 默认处理不了,需要调用 loader 加载器才可以正常打包,否则会报错!
    loader 加载器的作用:协助 webpack 打包处理特定的文件模块。比如:
    ⚫ css-loader 可以打包处理 .css 相关的文件
    ⚫ less-loader 可以打包处理 .less 相关的文件
    ⚫ babel-loader 可以打包处理 webpack 无法处理的高级 JS 语法

  2. loader 的调用过程
    在这里插入图片描述

  3. 打包处理 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 格式的图片

  1. 打包处理 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 的配置项如下:
在这里插入图片描述

打包发布

  1. 为什么要打包发布
    项目开发完成之后,需要使用 webpack 对项目进行打包发布,主要原因有以下两点:
    ① 开发环境下,打包生成的文件存放于内存中,无法获取到最终打包生成的文件
    ② 开发环境下,打包生成的文件不会进行代码压缩和性能优化
    为了让项目能够在生产环境中高性能的运行,因此需要对项目进行打包发布。

  2. 配置 webpack 的打包发布
    在 package.json 文件的 scripts 节点下,新增 build 命令如下:–mode 来指定模式为production模式,覆盖之前的开发模式的
    在这里插入图片描述

构建语句:

npm run build

  1. 把 JavaScript 文件统一生成到 js 目录中
    在 webpack.config.js 配置文件的 output 节点中,进行如下的配置:
    在这里插入图片描述
    这样就会在dist中生成一个js文件夹,里面存放bundle.js文件

  2. 把图片文件统一生成到 image 目录中
    修改 webpack.config.js 中的 url-loader 配置项,新增 outputPath 选项即可指定图片文件的输出路径:
    在这里插入图片描述

  3. 自动清理 dist 目录下的旧文件
    为了在每次打包发布时自动清理掉 dist 目录中的旧文件,可以安装并配置 clean-webpack-plugin 插件:
    在这里插入图片描述

Source Map的使用

前端项目在投入生产环境之前,都需要对 JavaScript 源代码进行压缩混淆,从而减小文件的体积,提高文件的加载效率。此时就不可避免的产生了另一个问题:
对压缩混淆之后的代码除错(debug)是一件极其困难的事情
⚫ 变量被替换成没有任何语义的名称
⚫ 空行和注释被剔除

  1. 什么是 Source Map
    Source Map 就是一个信息文件,里面储存着位置信息。也就是说,Source Map 文件中存储着压缩混淆后的代码,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大的方便后期的调试。
    他的作用说白了就是调试的时候,浏览器debug报错显示的行和源代码的能对应上了,方便调试(要配置devtool)。

  2. 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

  1. 构建用户界面
    • 用 vue 往 html 页面中填充数据,非常的方便
  2. 框架
    • 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
    • 要学习 vue,就是在学习 vue 框架中规定的用法!
    • vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
    • 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!

vue 的两个特性

  1. 数据驱动视图:

    • 数据的变化会驱动视图自动更新
    • 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
  2. 双向数据绑定:

    在网页中,form 表单负责采集数据,Ajax 负责提交数据

    • js 数据的变化,会被自动渲染到页面上
    • 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中

注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)

vue 指令

1. 内容渲染指令(v-text、插值表达式、v-html)
  1. v-text 指令的缺点:会覆盖元素内部原有的内容!比如下面的女会把性别给覆盖,所以它实际开发用的不多。
  2. {{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
  3. 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.org/images/logo.svg',
        index: 3
      }
    })
  </script>
</body>

</html>

效果:
在这里插入图片描述

3. 事件绑定(v-on简写为@,事件修饰符、按键修饰符)
  1. v-on: 简写是 @

  2. 语法格式为:

    <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>

效果:
在这里插入图片描述

  1. $event 的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event(因为如果没有参数是有事件对象e的,但是没参数就没有了)。例如:

    <button @click="add(3, $event)"></button>
    
    methods: {
       add(n, e) {
    			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
    			this.count += 1
       }
    }
    
  2. 事件修饰符:

    • .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.com" @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 的前提下,快速获取表单的数据。
应用场景:

  1. input 输入框
  • type=“radio”
  • type=“checkbox”
  • type=“xxxx”
  1. textarea
  2. 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)
  1. v-show 的原理是:动态为元素添加
  2. 或移除 display: none 样式,来实现元素的显示和隐藏
    • 如果要频繁的切换元素的显示状态,用 v-show 性能会更好
  3. v-if 的原理是:每次动态创建或移除元素,实现元素的显示和隐藏
    • 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好(因为此时不会被创建,没有dom操作,而v-show一定会被创建,只是后来会改变样式)

在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!

v-if 指令在使用的时候,有两种方式:还有if else 和 else等指令,具体看示例代码

  1. 直接给定一个布尔值 true 或 false

    <p v-if="true">被 v-if 控制的元素</p>
    
  2. 给 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.org/guide/migration/filters.html#migration-strategy

监听器

  1. 什么是 watch 侦听器
    watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
    语法格式如下:
    在这里插入图片描述
  2. 使用 watch 检测用户名是否可用
    在这里插入图片描述
  3. immediate 选项
    默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使
    用 immediate 选项。示例代码如下:
    在这里插入图片描述
  4. deep 选项
    如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项,代码示例如下:
    在这里插入图片描述
  5. 监听对象单个属性的变化
    在这里插入图片描述

计算属性

  1. 什么是计算属性
    计算属性指的是通过一系列运算之后,最终得到一个属性值。
    这个动态计算出来的属性值可以被模板结构或 methods 方法使用。示例代码如下
    在这里插入图片描述
  2. 计算属性的特点
    ① 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
    ② 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算

代码示例:

<!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 的基本使用:

  1. 发起 GET 请求:

    axios({
      // 请求方式
      method: 'GET',
      // 请求的地址
      url: 'http://www.liulongbin.top:3006/api/getbooks',
      // URL 中的查询参数
      params: {
        id: 1
      }
    }).then(function (result) {
      console.log(result)
    })
    
  2. 发起 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.org/zh/

安装和使用

vue-cli 是 npm 上的一个全局包,使用 npm install 命令,即可方便的把它安装到自己的电脑上:
npm install -g @vue/cli

基于 vue-cli 快速生成工程化的 Vue 项目:
vue create 项目的名称

  1. 在终端下运行如下的命令,创建指定名称的项目:

    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文件)

组件化开发介绍
  1. 什么是组件化开发
    组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。

  2. vue 中的组件化开发
    vue 是一个支持组件化开发的前端框架。
    vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。

  3. 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 入口文件中,通过 Vue.component() 方法,可以注册全局组件。示例代码如下:

在这里插入图片描述
使用的时候,就可以直接使用 MyCount标签了

props提高组件复用性
  1. 组件的 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页面上了。

当使用标签的释放使用别的组件的时候,就相当于创建了一个组件的实例。组件的创建是有一个生命周期的。

  1. 生命周期 & 生命周期函数

生命周期(Life Cycle)是指一个组件从创建 -> 运行 -> 销毁的整个阶段,强调的是一个时间段。

生命周期函数:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
注意:生命周期强调的是时间段,生命周期函数强调的是时间点。
生命周期函数就是在组件创建的某个阶段你去进行什么操作:
在这里插入图片描述
4. 生命周期图示

可以参考 vue 官方文档给出的“生命周期图示”,进一步理解组件生命周期执行的过程:
https://cn.vuejs.org/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>

组件之间的数据共享

  1. 组件之间的关系
    在项目开发中,组件之间的最常见的关系分为如下两种: ① 父子关系
    ② 兄弟关系

  2. 父子组件之间的数据共享
    父子组件之间的数据共享又分为:
    ① 父 -> 子共享数据
    ② 子 -> 父共享数据
    在这里插入图片描述
    演示:父向子传值 请注意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

  1. 什么是 ref 引用
    ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
    每个 vue 的组件实例上,都包含一个 $refs 对象(内置对象),里面存储着对应的 DOM 元素或组件的引用。默认情况下,
    组件的 $refs 指向一个空对象。
    在这里插入图片描述
  2. 使用 ref 引用 DOM 元素
    如果想要使用 ref 引用页面上的 DOM 元素,则可以按照如下的方式进行操作
    在这里插入图片描述
  3. 使用 ref 引用组件实例
    如果想要使用 ref 引用页面上的组件实例,则可以按照如下的方式进行操作:
    在这里插入图片描述
  4. 控制文本框和按钮的按需切换
    在这里插入图片描述
  5. 让文本框自动获得焦点
    在这里插入图片描述
  6. this.$nextTick(cb) 方法
    组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。通俗的理解是:等组件的DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。要不然组件还没更新,文本框dom还没有,你怎么去操作呢。
    在这里插入图片描述
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值