# vuex
store文件 vuex-dome
# mockjs
## 介绍
mock.js是模拟后端的数据,脱离后端独立开发,实现增删改查功能
安装mockjs :npm install mockjs -D
官网【http://mockjs.com/】
视频材料【https://www.bilibili.com/video/BV1rJ411u71A?from=search&seid=7985507154263444476】
材料【https://blog.csdn.net/gao_xu_520/article/details/79722821】
## 优点
官网描述的是
1.前后端分离
2.不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。
3.数据类型丰富
4.通过随机数据,模拟各种场景。
## API
简单的使用:testMock.js
## 使用
### 直接使用时
a. 在 main.js
import axios from 'axios'
// 全局挂载
Vue.prototype.$http = axios
import '../mock/testMock.js'
b.创建mock/testMock.js
// 导入模拟假数据的包
import Mock, { Random } from 'mockjs'
//生成随机id
let id = Mock.mock('@id');
console.log('测试mock随机生成 id:',id)
// 自定义一个数组--随机获取这个数组里边的值--引用Random
// 创建自定义Mock函数
Random.extend({
// 自定义函数名:function 函数
fruit: function () {
const arr = ['榴莲', '菠萝蜜', '椰子', '苹果', '菠萝']
return this.pick(arr)
}
})
// 通过Mock.mock()来模拟API接口----GET请求
Mock.mock('/api/goodslist', 'get', {
status: 200,
message: '获取商品列表成功',
// 生成5到10条,或者直接data|5条数据
'data|5-10': [
{
// mock自增函数@increment-从1开始
id: '@increment(1)',
// 随机返回中文文字-显示名字
name: '@cword(2, 8)',
// 随机返回一个自然数
price: '@natural(2, 10)',
count: '@natural(100, 999)',
// 建议使用 data字符串压缩64格式,你建议url地址请求
img: '@dataImage(78x78)'
}
]
})
c. 组件中使用
// 模拟异步get
async getGoodsList () {
const { data: res } = await this.$http.get('/api/goodslist')
console.log("mock 数据:", res);
},
### 读取json 数据
json5 解决json文件,无法添加注释问题
cnpm install json5 -D
main.js
import axios from 'axios'
// 全局挂载
Vue.prototype.$http = axios
a. 创建mock/userInfo.json5
{
// mock自增函数@increment-从1开始
id: '@increment(1)',
// 随机返回中文文字-显示名字
name: '@cname(2, 8)',
// 随机返回一个自然数
data: '@date()',
description:"@paragraph()",
ip:"@ip()",
email:"@email()"
}
b. 创建mock/testJSON5.js
const fs = require('fs')// fs文件读取
const path = require('path')// 文件路径
const Mock = require('mockjs')
const JSON5 = require('json5')// json文件转换
/**
* 读取 json 文件
* getJsonFile 定义了如何读取json文件并解析成数据对象
* @param {*} filePath 文件地址
*/
function getJsonFile(filePath) {
// 读取指定json文件
var json = fs.readFileSync(path.join(__dirname, filePath), 'utf-8');
// 解析并返回json 对象
return JSON5.parse(json);
}
module.exports = function (app) {
//监听http请求
app.get('/user/userinfo', function (rep, res) {
//每次响应请求时读取mock data 的json文件
// 读取json对象
var json = getJsonFile('./userInfo.json5');
//将json 传入Mock.mock方法中,生成数据,并返回
res.json(Mock.mock(json));
})
}
c. 在vue.config.js
module.exports = {
devServer: {
///before 中间件
before: require('./mock/testJSON5.js')
},
}
d. 组件使用 index.vue
mounted() {
this.$http
.get("/user/userinfo")
.then(res => {
console.log("组件中获取 数据:", res);
})
.catch(err => {
console.log("报错:", err);
});
}
浏览器输入地址,访问到devServer,返回静态资源,页面中请求axios.get('url), ajax请求服务器,但是被我们配置的devServer给拦截了,然后devServer 会调用mock.js模块,产生随机数据,然后随机数据产生于json5的文件。产生的随机数据就返回给了页面
补充:可以配置环境变量
### mock-server 服务
如果用了后端请求,尽量 不要使用mock-server.js 不然会影响到后端接口请求
### 设置环境下的配置
.env.development 开发环境下的配置文件
.env.production 生产环境下的配置文件
.env.development 文件
# 开发环境配置
ENV_MOCKS = 'mock'、
main.js
/**
*您想将MockJs用于mock api
*可以执行:mockXHR()
*目前MockJs将用于生产环境,
*请在上线前删除!
*/
//开发环境使用
import {
mockXHR
} from '../mock'
if (process.env.ENV_MOCKS === 'mock') {
mockXHR()
console.log('ssss')
}
### 增删改查
main.js
import {
mockXHR
} from '../mock'
if (process.env.ENV_MOCKS === 'mock') {
mockXHR()
}
mock/table.js
view/mockTest/index.vue
查看【https://www.yht7.com/news/124109,https://www.cnblogs.com/niejunchan/p/13022477.html】
### jQuery中mock的使用
暂不分析
视频教程【https://www.bilibili.com/video/BV1PE411A7U6?p=10】
## 注意问题
1. 在mock配置中使用mock造数据的时候,采用dataImage生成图片,但是编译的时候会报错:module.require is not a function,去掉dataImage就没问题
# 代码规范注意点
规范 :[前端各类规范集合](https://github.com/ecomfe/spec)
## json5 解决json文件,无法添加注释问题
cnpm install json5 -D
## 使用对象代替 if 及 switch
在很多情况下,我们经常会遇到循环判断执行赋值操作的场景,一般我们都会使用 if 及 switch 的条件判断,如果符合则执行赋值,不符合则进入下个判断,比如:
let name = 'lisi';
let age = 18;
if (name === 'zhangsan') {
age = 21;
} else if (name === 'lisi') {
age = 18;
} else if (name === 'wangwu') {
age = 12;
}
// 或者
switch(name) {
case 'zhangsan':
age = 21;
break
case 'lisi':
age = 18;
break
case 'wangwu':
age = 12;
break
}
这样的写法不仅冗余,而且代码执行效率不高,我们可以使用对象的形式简写:
let name = 'lisi';
let obj = {
zhangsan: 21,
lisi: 18,
wangwu: 12
};
let age = obj[name] || 18;
## 使用 Array.from 快速生成数组
一般我们生成一个有规律的数组会使用循环插入的方法,比如使用时间选择插件时,我们可能需要将小时数存放在数组中:
let hours = [];
for (let i = 0; i < 24; i++) {
hours.push(i + '时');
}
如果使用 Array.from 我们可以简写为:
let hours = Array.from({ length: 24 }, (value, index) => index + '时');
## 使用 computed 代替 watch
很多时候页面会出现 watch 的滥用而导致一系列问题的产生,而通常更好的办法是使用 computed 属性,首先需要区别它们有什么区别:
watch:当监测的属性变化时会自动执行对应的回调函数
computed:计算的属性只有在它的相关依赖发生改变时才会重新求值
其实它们在功能上还是有所区别的,但是有时候可以实现同样的效果,而 computed 会更胜一筹,比如:
{{ fullName }}
{{ fullName2 }}
上方我们通过对比可以看到,在处理多数据联动的情况下,使用 computed 会更加合理一点。
computed 监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算;而 watch 监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。
## 统一管理缓存变量
在项目中或多或少会使用浏览器缓存,比如 sessionStorage 和 localStorage,当一个项目中存在很多这样的缓存存取情况的时候就会变得难以维护和管理,因为其就像全局变量一样散落在项目的各个地方,这时候我们应该将这些变量统一管理起来,放到一个或多个文件中去,比如:
/* types.js */
export const USER_NAME = 'userName';
export const TOKEN = 'token';
在需要存取的时候,直接引用:
import { USER_NAME, TOKEN } from '../types.js'
sessionStorage[USER_NAME] = '张三';
localStorage[TOKEN] = 'xxx';
使用这种方法的好处在于一旦我们需要修改变量名,直接修改管理文件中的值即可,无需修改使用它的页面,同时这也可以避免命名冲突等问题的出现,这类似于 vuex 中 mutations 变量的管理。
## 使用 setTimeout 代替 setInterval
一般情况下我们在项目里不建议使用 setInterval,因为其会存在代码的执行间隔比预期小以及 “丢帧” 的现象,原因在于其本身的实现逻辑。很多人会认为 setInterval 中第二个时间参数的作用是经过该毫秒数执行回调方法,其实不然,其真正的作用是经过该毫秒数将回调方法放置到队列中去,但是如果队列中存在正在执行的方法,其会等待之前的方法完毕再执行,如果存在还未执行的代码实例,其不会插入到队列中去,也就产生了 “丢帧”。
而 setTimeout 并不会出现这样的现象,因为每一次调用都会产生了一个新定时器,同时在前一个定时器代码执行完之前,不会向队列插入新的定时器代码。
// 该定时器实际会在 3s 后立即触发下一次回调
setInterval(() => {
// 执行完这里的代码需要 2s
}, 1000);
// 使用 setTimeout 改写,4秒后触发下一次回调
let doSometing = () => {
// 执行完这里的代码需要 2s
setTimeout(doSometing, 1000);
}
doSometing();
## 不要使用 for in 循环来遍历数组
大家应该都知道 for in 循环是用于遍历对象的,但它可以用来遍历数组吗?答案是可以的,因为数组在某种意义上也是对象,但是如果用其遍历数组会存在一些隐患:其会遍历数组原型链上的属性。
let arr = [1, 2];
for (let key in arr) {
console.log(arr[key]); // 会正常打印 1, 2
}
// 但是如果在 Array 原型链上添加一个方法
Array.prototype.test = function() {};
for (let key in arr) {
console.log(arr[key]); // 此时会打印 1, 2, ƒ () {}
}
因为我们不能保证项目代码中不会对数组原型链进行操作,也不能保证引入的第三方库不对其进行操作,所以不要使用 for in 循环来遍历数组。
# 学会编写可复用性模块
## 减少重复的代码
通过将 data.obj.items 的值赋值给变量 values 来实现了复用,此时修改 items 为 lists 的话我们只需修改一处地方即可,不管是维护成本还是代码可读性上,复用的优势都显而易见。
let person = [];
for (let i = 0; i < data.obj.items.length; i++) {
person.push({
name: data.obj.items[i].name,
age: data.obj.items[i].age
});
}
改成:
let person = [];
let values = data.obj.items;
for (let i = 0; i < values.length; i++) {
person.push({
name: values[i].name,
age: values[i].age
});
}
## 封装成一个函数
除了使用变量的赋值缓存使用来解决数据的重复读取外,我们在开发过程中重复性更多的也许是功能点的重复,比如:
上述代码的重复功能点在于截取输入框中第二个字符开始到最后的值并把它们转化成大写字母,像这样很简单的操作虽然重复使用也不会出现太大的问题,但是如果是代码量较多的操作呢?重复书写相同功能的代码是一种不经过大脑思考的行为,我们需要对其进行优化,这里我们可以把功能点封装成一个函数:
export default {
methods: {
sliceUpperCase(val) {
return val.slice(1).toUpperCase()
}
}
}
如此我们只要在用到该方法的地方调用即可,将值传入其中并返回新值。当然像在双花括号插值和 v-bind 表达式中重复的功能点我们可以封装成过滤器比较合适:
// 单文件组件注册过滤器
filters: {
sliceUpperCase(val) {
return val.slice(1).toUpperCase()
}
}
// 全局注册过滤器
Vue.filter('sliceUpperCase', function (val) {
return val.slice(1).toUpperCase()
})
然后在 html 中使用“管道”符进行过滤:
{{ str1 | toUpperCase }}
{{ str2 | toUpperCase }}
## 封装成一个插件
[Vue 插件编写与实战](https://mp.weixin.qq.com/s/Aqgh7Dkialhm9v8U0wBuqg)
Vue 提供给了我们一个 install 方法来编写插件,使用该方法中的第一个 Vue 构造器参数可以为项目添加全局方法、资源、选项等
/* toast.js */
import ToastComponent from './toast.vue' // 引入组件
let $vm
export default {
install(Vue, options) {
// 判断实例是否存在
if (!$vm) {
const ToastPlugin = Vue.extend(ToastComponent); // 创建一个“扩展实例构造器”
// 创建 $vm 实例
$vm = new ToastPlugin({
el: document.createElement('div') // 声明挂载元素
});
document.body.appendChild($vm.$el); // 把 toast 组件的 DOM 添加到 body 里
}
// 给 toast 设置自定义文案和时间
let toast = (text, duration) => {
$vm.text = text;
$vm.duration = duration;
// 在指定 duration 之后让 toast 消失
setTimeout(() => {
$vm.isShow = false;
}, $vm.duration);
}
// 判断 Vue.$toast 是否存在
if (!Vue.$toast) {
Vue.$toast = toast;
}
Vue.prototype.$toast = Vue.$toast; // 全局添加 $toast 事件
}
}
成功编写完插件的 JS 脚本后,我们在入口文件中需要通过 Vue.use() 来注册一下该插件:
import Toast from '@/widgets/toast/toast.js'
Vue.use(Toast); // 注册 Toast
最后我们在需要调用它的地方直接传入配置项使用即可,比如:
this.$toast('Hello World', 2000);
## 数据驱动
做数据是改变了,但是页面并没有因此重新渲染,这是为什么呢?
由于 Vue 检测不到数组变动,因此页面便无法重绘。同样 Vue 也不能检测对象属性的添加或删除,需要使用 Vue.set(object, key, value) 方法来实现。
// 设置数组值
setPuzzle(index, num) {
let curNum = this.puzzles[index]
this.$set(this.puzzles, index + num, curNum)
this.$set(this.puzzles, index, '')
},
# Vue API 盲点解析
# vue cli 模式和环境变量
官网【https://cli.vuejs.org/zh/guide/mode-and-env.html】
根据不同的环境 设置不同的变量
比如 开发环境配置
创建 .env.development 文件
就可以在里面定义变量
然后在js 里面直接获取process.env.XX
# 滤器(filter)的使用
https://blog.csdn.net/badmoonc/article/details/81485803
{{ userInfo.sex | formatDate }}
import Formatter from "@/utils/initialData";
filters: {
...Formatter
},
const formatter = {
// 性别1、男 2、女 9、未说明性别
sex: (val) => {
let result = "";
switch (val) {
case 1:
result = "男";
break;
case 2:
result = "女";
break;
case 9:
result = "未知";
break;
}
return result;
}
}
export default formatter
例如:
过滤器filter :view/filter/index.vue
- 姓名:{{item.name}},性别:{{item.sex | sexType}},出生日期:{{item.datatime | formatDate}}
# 静态请求及underscore JS使用
view/localData/index.vue
# 登录拦截
axios:[](https://github.com/superman66/vue-axios-github)
## 拦截器
1. 创建axios实例
const instance = axios.create({
timeout: 1000 * 12
});
2. 设置post请求头
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
3.http request 拦截器
/**
* 请求拦截器
* 每次请求前,如果存在token则在请求头中携带token
*/
instance.interceptors.request.use(
config => {
// 登录流程控制中,根据本地是否存在token判断用户的登录情况
// 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
// 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
// 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
const token = store.state.token || localStorage.getItem('token');
token && (config.headers.Authorization = token);
return config;
},
error => Promise.error(error)
)
4.http response 拦截器
instance.interceptors.response.use(
// 请求成功
res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
// 请求失败
error => {
const {
response
} = error;
if (response) {
// 请求已发出,但是不在2xx的范围
errorHandle(response.status, response.data.message);
return Promise.reject(response);
} else {
// 处理断网的情况
// eg:请求超时或断网时,更新state的network状态
// network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
// 关于断网组件中的刷新重新获取数据,会在断网组件中说明
// store.commit('changeNetwork', false);
}
}
);
/**
* 跳转登录页
* 携带当前页面路由,以期在登录页面完成登录后返回当前页面
*/
const toLogin = () => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}
/**
* 请求失败后的错误统一处理
* @param {Number} status 请求失败的状态码
*/
const errorHandle = (status, other) => {
// 状态码判断
switch (status) {
// 401: 未登录状态,跳转登录页
case 401:
toLogin();
break;
// 403 token过期
// 清除token并跳转登录页
case 403:
tip('登录过期,请重新登录');
localStorage.removeItem('token');
// store.commit('loginSuccess', null);
setTimeout(() => {
toLogin();
}, 1000);
break;
// 404请求不存在
case 404:
tip('请求的资源不存在');
break;
default:
console.log(other);
}
}
## 路由拦截
//登录拦截
router.beforeEach((to, from, next) => {
console.log('state', store.state)
if (store.state.userInfo || to.path === "/login") {
next()
} else {
next({
path: "/login"
})
}
})
# 问题点
## 一些 npm 包名为什么要用 @ 开头?
新的包名规则对于包的命名方式进行了一些修改,为的是更好的防御「误植」攻击,同时帮助包开发者们挑选出更加合适的包名。
如果因为你起的包名与现有的包名太相近而被阻止发布这个包,那么找到一个独一无二包名最简单方法就是使用你的作用域。你可以使用@+你的 npm 用户名加在包名前面将包划到你的 npm 账户作用域下。
如@xiaomajia/xxx-xxxx
## webpack 在 CLI 3 中的应用
https://blog.csdn.net/guozhangqiang/article/details/87197870
## webacpk ,none ,chrome
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,
webapck 是支持node api
所以 chrome浏览器能访问的,node 也可以,webpack 也可以,webpack
export default ,import 这个是es6 不是node ,所以在node ,否则是普通页面 是不能访问的,
如果要访问的必须引用babel库的 ,如 :babel-plugin-dynamic-import-node 就可以 用于引入import