扩展

环境搭建

安装脚手架

vue cli

1# 安装vue cli脚手架
2npm install -g @vue/cli
3# 升级vue cli脚手架版本
4npm update -g @vue/cli
5
6#查看vue版本
7vue --version
8vue -V
9
10# 命令行创建项目
11vue create project_name
12vue create . # 当前目录下创建
13# 可视化创建项目
14vue ui

创建项目

1# 命令行创建项目
2vue create project_name
3# 当前目录下创建
4vue create . 
5
6# 1.选择手动选择功能并回车
7Vue CLI v5.0.8
8? Please pick a preset:
9  Default ([Vue 3] babel, eslint)
10  Default ([Vue 2] babel, eslint)
11❯ Manually select features
12
13# 2.按空格选中或取消选中,按回车下一步
14Vue CLI v5.0.8
15? Please pick a preset: Manually select features
16? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
17 ◉ Babel
18 ◯ TypeScript
19 ◯ Progressive Web App (PWA) Support
20 ◉ Router
21 ◉ Vuex
22 ◉ CSS Pre-processors
23❯◉ Linter / Formatter
24 ◯ Unit Testing
25 ◯ E2E Testing
26 
27# 3.选择Vue版本,选择2.x并回车
28Vue CLI v5.0.8
29? Please pick a preset: Manually select features
30? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter
31? Choose a version of Vue.js that you want to start the project with
32  3.x
332.x
34
35# 4.是否使用history模式的路由,输入Y并回车
36Vue CLI v5.0.8
37? Please pick a preset: Manually select features
38? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter
39? Choose a version of Vue.js that you want to start the project with 2.x
40? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y
41
42# 5.选择css预处理器,选择Less并回车
43? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):
44  Sass/SCSS (with dart-sass)
45❯ Less
46  Stylus
47  
48# 6.选择ESLint,选择默认并回车
49? Pick a linter / formatter config: (Use arrow keys)
50❯ ESLint with error prevention only
51  ESLint + Airbnb config
52  ESLint + Standard config
53  ESLint + Prettier
54
55# 7.在什么时候检测Lint,选择默认并回车
56? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
57❯◉ Lint on save
58 ◯ Lint and fix on commit
59 
60# 8.将这个配置文件放在哪里?选择默认并回车
61? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
62❯ In dedicated config files
63  In package.json
64  
65# 9.是否保存刚才的预设,(将刚才的配置作为模版,下次选择可直接创建,免去从头到尾一项一项配置的烦恼)
66 Save this as a preset for future projects? (y/N) N
67
68# 10.若9选择Y,这里需要为预设模版起名字
69 Save preset as:

启动项目

1cd project_name && npm run serve

环境变量

我们实际开发中,往往有多种环境,如

  • development模式用于 vue-cli-service serve
  • production模式用于vue-cli-service build 和 vue-cli-service test:e2e
  • test模式用于vue-cli-service test:unit

甚至还有其他的,这里我们只讨论development和production。

此处有几个需要注意的点:

1.一个模式可以包含多个环境变量

2.每个模式都会将环境变量中 NODE_ENV 的值设置为模式的名称

3.可以通过.env文件增加后缀来设置某个模式下特有的环境变量

4.为一个特定模式准备的环境文件(例如 .env.production)将会比一般环境文件(例如 .env)拥有更高的优先级

1.env							# 在所有环境中被载入
2.env.local				# 在所有环境中被载入,但会被 git 忽略
3.env.[mode]				# 只在指定的模式中被载入,优先级高于 .env 和 .env.local
4.env.[mode].local	# 只在指定的模式中被载入,但会被 git 忽略,优先级高于 .env 和 .env.local

一般环境变量文件都创建在根目录下,我们创建以下两份:

.env.development 文件:

NODE_ENV=development VUE_APP_BASE_URL=/

.env.production 文件:

NODE_ENV=production VUE_APP_BASE_URL='http://www.xiaosutongxue.com'

使用场景

1const { defineConfig } = require('@vue/cli-service')
2module.exports = defineConfig({
3  devServer:{
4    // 在Vue中通过process.env.变量名获取环境变量
5    // 使用机制:当项目以生产环境启动时,process.env.VUE_APP_BASE_URL这个值就在.env.production文件中寻找
6    proxy: process.env.VUE_APP_BASE_URL
7  }
8})

怎么以生产环境启动呢?

配置 package.json 中的启动命令

1"scripts": {
2"serve": "vue-cli-service serve --mode production",
3"build": "vue-cli-service build"
4}

@路径配置

vscode安装 Path Intellisense 插件

打开设置-首选项-搜索 Path Intellisense -打开 settings.json ,添加

1"path-intellisense.mappings":{
2  "@": "${workspaceRoot}/src"
3}

在项目 package.json 所在同级目录下创建文件 jsconfig.json

1{
2    "compilerOptions": {
3        "target": "ES6",
4        "module": "commonjs",
5        "allowSyntheticDefaultImports": true,
6        "baseUrl": "./",
7        "paths": {
8          "@/*": ["src/*"]
9        }
10    },
11    "exclude": [
12        "node_modules"
13    ]
14}

代码片段

1{
2  "vue2": {
3    "prefix": "v2",
4    "body": [
5      "<template>",
6      "\t<div>\n",
7      "\t</div>",
8      "</template>\n",
9      "<script>",
10      "export default {",
11      "\tdata() {",
12      "\t\treturn {\n",
13      "\t\t}",
14      "\t},",
15      "\tcomponents: {\n",
16      "\t},",
17      "\tcomputed: {\n",
18      "\t},",
19      "\tcreated() {\n",
20      "\t},",
21      "\tmethods: {\n",
22      "\t},",
23      "}",
24      "</script>\n",
25      "<style lang=\"less\" scoped>",
26      "</style>"
27    ]
28  }
29}

Vue2双向绑定原理

Object.defineProperty() 是ES5中一个无法shim的特性,可以用来给对象设置属性

一个shim是一个库,它将一个新的API引入到一个旧的环境中,而且仅靠旧环境中已有的手段实现。 比如:google和github上都有一段用于兼容ie等低版本浏览器的html标签库 html5shiv,ps: shim有时也叫shiv。

第一种方法:

1let obj = {
2  name: "xiaosutongxue"
3}
4
5// 普通添加属性的方式
6obj.age = 27
7
8// Object.defineProperty()添加属性的方式
9Object.defineProperty(obj, "wish", {
10  value: "code better",
11  writable: true,	// 设置obj的wish属性可以被修改,默认值是false,不可被修改
12  configurable: true,	// 设置obj的wish属性可以被删除,默认是false,不可被删除
13  enumerable: true	// 设置obj的wish属性可以被遍历,默认是false,不可被遍历
14});
15
16console.log(obj)

第二种方法:

1let obj = {};
2let val = 10;
3Object.defineProperty(obj, "name", {
4  // 思考:此处get()什么时候执行,执行后返回了什么?
5  get() {
6    // 当获取name属性的时候,会执行这里的代码
7    console.log("get执行了");
8    // return返回的数据就是obj.name的值
9    return val;
10  },
11  set(newValue) {
12    // 当给name属性赋值的时候,会执行这里的代码
13    console.log("set执行了", newValue);
14    // 将新数据赋值给val,这样下次执行get()时,返回的就是新值
15    val = newValue;
16  },
17});
18
19console.log(obj.name); // 执行get()
20obj.name = "Vue"; // 触发set()
21console.log(obj.name); // 执行get()
22
23/** 控制台打印结果
24* get执行了
25* 10
26* set执行了 Vue
27* get执行了
28* Vue
29*/

模拟双向绑定

1<input type="text" id="inp" />
2<p id="pp"></p>
1// 准备一个对象,模拟data这个对象,往里面添加一个txtVal属性
2let obj = {};
3let val = "初始值";
4Object.defineProperty(obj, "txtVal", {
5  get() {
6    return val;
7  },
8  set(newValue) {
9    // 在set方法进行数据劫持
10    // 此处set()是第三方,负责通知每一个订阅者改数据了
11    inp.value = newValue;
12    pp.innerHTML = newValue;
13    val = newValue;
14  },
15});
16
17// 两个订阅者,都订阅了obj.txtVal
18inp.value = obj.txtVal;
19pp.innerHTML = obj.txtVal;
20
21// v-model可以用value属性和input事件代替
22inp.addEventListener("input", (e) => {
23  // 一边输入的时候,一边修改txtVal的值
24  // obj.txtVal = 用户输入的值
25  // 这里是发布者,发布新的数据
26  obj.txtVal = e.target.value; // 触发set()
27});

双向绑定原理

借助Object.defineProperty()的set()对数据进行劫持,并结合发布-订阅者模式来实现双向绑定

Vue2的数组内元素的修改失效问题

为什么会有这种bug

原理:Vue2的双向数据绑定原理用的是Object.defineProperty(),修改引用类型的数据(数组和对象)的时候没有办法触发set,所以最终导致的现象就是数据能够修改,但是界面修改不了。

Vue提供了 this.$set() 方法,可以用来修改引用类型的数据,同时进行界面修改。

this.$set(数组名,要修改元素的下标,新的值)

SPA

Single Page Application,中文:单页应用。

其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由,也就是前端来维护一套路由规则。

前端路由的核心是什么呢?

改变URL,但是页面不进行整体的刷新

MVVM

1.view层---body内的标签

视图层,在前端里面就是我们常说的DOM层,主要作用是给用户展示各种信息

2.Model层---new Vue对象

数据层,数据可能是我们自定义的数据,或者是从网络请求下来的数据

3.ViewModel层---vue.js文件

视图模型层,是view层和model层沟通的桥梁;

一方面它实现了数据绑定,将model的改变实时反应到view中;另一方面它实现了DOM监听,当DOM发生改变可以对应改变数据(Data)

scoped的作用

scoped会在元素上添加唯一的属性(data-v-x形式),css编译后也会加上属性选择器,从而达到限制作用域的目的

模块化

AMD、CMD,鼻祖级别的,现在半淘汰状态了

模块化目前比较流行的就是 CommonJs规范ES6模块化

Commonjs

导出是导出,导入是导入两者没有必然关系

只要导出正确,导入使哪种方式都行,但尽量配套使用

导出

1var num = 10;
2var obj = {username:"su"}
3function func(){
4  return 123
5}
6
7//整体导出,支持对象简写
8module.exports = {
9  num,
10  obj,
11  func
12}
13
14//按需导出 不建议这么写,意义不大
15module.exports.num = num;
16module.exports.obj = obj;
17module.exports.func = func;

导入

1//commonjs
2var test = require('@/路径')
3//es6
4import test from '@/路径'

module.exports导出,本质上是在导出什么?

在导出exports

只写module 或者 module.exports;导出都是一个空对象,因为引入一个js文件,本质上就是在引入一个空对象

{ module:{ exports:{ num, obj, func } } }

ES6

导出

1var num = 10;
2var obj = {username:"su"}
3function func(){
4  return 123
5}
6
7//整体导出
8export default {
9  num,
10  obj,
11  func
12}
13
14//按需导出,
15export var num = 10;

导入

1//es6 默认引入export下的default
2import test from '@/路径'
3
4//commonjs 默认引入的是export对象
5var test = require('@/路径')
6
7
8//按需导入
9import {num} from '@/路径'
  • export:通过export方式导出,在导入的时候需要加,且不能换名字(导出啥名,导入就啥名,名字加到
    • import {aaa,bbb,ccc导出的时候的名字} from "路径+文件名"
  • export default:通过export default方式导出,导入时不需要加
    • import 名字 from "路径+文件名"
  • 使用场景
    • 导出单个值就用 export default
    • 导出多个值就用 export