第一节、核心vue配置
一定要记住:每次更改了配置文件需要手动重新开服
核心配置文件:项目根目录下vue.config.js (等价于webpack.config.js)
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
//根据浏览器自动采取是否把ES6转为ES5,兼容
transpileDependencies: true,
//配置路径 ,能够避免打包后js找不到路径(推荐)
publicPath:"./",
//去掉map文件(map文件是用来查找源码出问题的行数的)
productionSourceMap:false,
//别名,为某一段路径取别名(可以用于打包之后和开发阶段)
configureWebpack:{
resolve:{
alias:{
//"别名":"路径"
"xxx":"@/views/stu"
}
}
},
})
懒加载分组打包
打包的js个数会随着懒加载组件的增加而增加,—》打包之后的js文件个数增加----》性能降低
解决:通过分组配置来合并懒加载的文件个数
router/index.js
let Login=()=>import(/*webpackChunkName:'group-user'*/'@/views/Login.vue')
let Register=()=>import(/*webpackChunkName:'group-user'*/'@/views/Register.vue')
本地服务
//本地服务(只能用于开发阶段)
devServer: {
port: 8080,
//代理 前端处理跨域
//浏览器有同源策略(协议、ip地址、端口) ---》 ajax
//生产阶段(上线)一定是后端来处理
proxy: {
// "/users": {
// //需要代理的地址(和axios的baseURL两者选其一)
// //代理生效的条件:以后ajax必须以/自定义标识开头
// //ajax的路径写: /users 等价于 http://localhost:3000/user
// //错误的想法: /users 等价于 http://localhost:3000
// target: "http://localhost:3000"
// },
// "/stus": {
// target: "http://localhost:3000"
// },
// "/clas": {
// target: "http://localhost:3000"
// }
"/api":{
target: "http://localhost:3000",
//关闭ws报错提示(对程序没有影响)
ws:false,
//重写路径
pathRewrite:{
// "^/标识" :"/替换的路径"
"^/api":""
}
}
// /api/stus/getAllByPage
// http://localhost:3000/aa/stus/getAllByPage
}
注意:浏览器的F12调试器是无法识别代理
第二节、多环境
能够通过配置,让项目在不同环境中自动读取不同的变量
- 开发环境 --development
- 测试环境 --test
- 生产环境 --production
面临问题
开发:http://localhost:3000
生产:http://localhost:4000
为了今后切换地址方便,我们可以配置多环境,程序自动读取对应地址
步骤
1 在项目根目录下新建两个文件(文件名是固定)
1 .env.development npm run serve 程序会读取该文件
2 .env.production npm run build 程序会读取该文件
3 .env npm run serve和npm run build 程序都会读取该文件
2 语法
2.1 定义
1 NODE_ENV=值 只能写一次 ,一般来说值就是标题,比如 生产环境 、开发环境
2 VUE_APP_自定义变量=值 能够写多次,值一般就是今后不同环境对应不同的数据,比如url地址等...
注释: # ....
注意:中间没有任何符号
2.2 读取
今后可以在该项目的任意文件中读取到环境文件的变量(vue、js。。)
process.env.NODE_ENV
process.env.VUE_APP_自定义变量
自定义环境文件
1 依然在项目根目录中新建
2 取名规范为:.env.xx ,xx就是自定义名字
3 配置package.json中的 scripts属性
语法:
"自定义命令名" :"vue-cli-service serve/build --mode .env的后缀名"
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"serve-a":"vue-cli-service serve --mode a", ---》npm run serve-a
"build-b":"vue-cli-service build --mode b" ---> npm run build-b
},
第三节、vue响应式原理
vue的响应式是什么东西?
数据驱动基于响应式,当vue检测到data数据发生变化时,则会自动去渲染页面
1 侦查数据的变化—数据劫持
该方法可以劫持对象的属性(实现了监听),一旦数据的属性发生更改或者被访问,都会被监听到
Object.defineProperty()来劫持data定义数据,
demo1:数据劫持的基本操作
let user={
name:"张三"
}
let value=user.name
//劫持
//参数1.被劫持的对象
//参数2.被劫持的对象的具体的属性
//参数3:触发监听后的业务
Object.defineProperty(user,'name',{
//一旦user的name被访问,那么就会触发get方法
get(){
console.log("有人在访问user的name属性");
return value
},
//一旦user的name被更新时,那么就会触发set方法
set(val){
console.log("有人在修改user的name属性,更新后的值是:"+val);
value=val
}
})
console.log(user.name);//张三
user.name='李四'
console.log(user.name);
demo2:封装数据劫持
//封装方法---数据劫持
function defineReactive(data, key, value) {
//参数1.被劫持的对象
//参数2.被劫持的对象的具体的属性
//参数3:触发监听后的业务
Object.defineProperty(data, key, {
//一旦user的name被访问,那么就会触发get方法
get() {
console.log("有人在访问user的name属性");
return value
},
//一旦user的name被更新时,那么就会触发set方法
set(val) {
console.log("有人在修改user的name属性,更新后的值是:" + val);
value = val
}
})
}
let user = {
name: "张三"
}
//劫持user的name属性
defineReactive(user,'name',user.name)
console.log(user.name);
user.name = '李四'
console.log(user.name);
demo3: 再次封装方法—数据劫持
//封装方法---数据劫持
function defineReactive(data, key, value) {
//参数1.被劫持的对象
//参数2.被劫持的对象的具体的属性
//参数3:触发监听后的业务
Object.defineProperty(data, key, {
//一旦user的name被访问,那么就会触发get方法
get() {
console.log("有人在访问user的name属性");
return value
},
//一旦user的name被更新时,那么就会触发set方法
set(val) {
console.log("有人在修改user的name属性,更新后的值是:" + val);
value = val
}
})
}
//再次封装方法---数据劫持 (劫持多个属性)
function observer(data){
for(let k in data){
defineReactive(data,k,data[k])
}
}
let user = {
name: "张三",
age:18
}
//劫持user的所有属性
observer(user)
console.log(user.name);
user.name = '李四'
console.log(user.name);
console.log(user.age);
user.age = 10
console.log(user.age);
demo4 递归封装(解决属性是引用类型的情况)
//封装方法---数据劫持
//user,dog,{type:"哈士奇"}
function defineReactive(data, key, value) {
//递归
observer(value)
//参数1.被劫持的对象
//参数2.被劫持的对象的具体的属性
//参数3:触发监听后的业务
Object.defineProperty(data, key, {
//一旦user的name被访问,那么就会触发get方法
get() {
console.log("有人在访问对象的"+key+"属性");
return value
},
//一旦user的name被更新时,那么就会触发set方法
set(val) {
console.log("有人在修改对象的属性,更新后的值是:" + val);
value = val
}
})
}
//再次封装方法---数据劫持 (劫持多个属性)
function observer(data){
//递归结束条件,如果某个属性不是对象
if(typeof data != 'object'){
return data;
}
for(let k in data){
defineReactive(data,k,data[k])
}
}
let user = {
name: "张三",
age:18 ,
dog:{
type:"哈士奇"
}
}
//劫持user的所有属性
observer(user)
// console.log(user.dog.type);
// user.dog.type = '金毛'
// console.log(user.dog.type);
// console.log(user.name);
// user.name = '李四'
// console.log(user.name);
// console.log(user.age);
// user.age = 10
// console.log(user.age);
2 收集视图依赖了哪些数据—依赖收集 (初始化)
引入了两个角色:
1 watcher观察者, 有几个{{}}那么就会new出几个观察者对象
每一个watcher对象都拥有 render方法(渲染)
2 dep订阅者 ,有几个属性就new出几个订阅者对象
每一个dep对象拥有一个数组属性(subs)以及一个方法(notify)
比如订阅者age 就拥有一个数组,[watcher对象-age,watcher对象-age]
订阅者name 就拥有一个数组, [watcher对象-name,watcher对象-name,watcher对象-name]
notify方法, 该方法可以通知dep订阅者对应的数组中的每一个watcher对象执行render方法
Object.defineProperty(xx, xx, {
//一旦user的name被访问,那么就会触发get方法
get() {
依赖收集操作
},
//一旦user的name被更新时,那么就会触发set方法
set(val) {
}
})
demo:
5个 watcher
2个 dep
{{name}} {{name}} {{name}}
{{age}} {{age}}
data:{
name:"aaa",
age:18
}
3 数据变化时,dep会”通知“需要更新的视图部分,并进行更新-----发布订阅
一旦data属性发生修改,则
Object.defineProperty(xx, xx, {
//一旦user的name被访问,那么就会触发get方法
get() {
依赖收集操作
},
//一旦user的name被更新时,那么就会触发set方法
set(val) {
通知修改操作
}
})
我们把vue响应式原理也称之为 观察者模式、订阅/观察 模式