***论阅读源码的重要性***
/*解决https下面无法加载http路径图片的问题
<meta http-equiv ="Content-Security-Policy" content="upgrade-insecure-requests">
es6新语法:
箭头函数
在传统函数的写法上,去掉function关键字, 并且在参数列表的()与{} 函数体之间加上=>,若该函数没有参数,小括号不能省略。
var foo = function(){};
var foo =()=> {};
foo();
模板字符串
而在ES6的语法当中,采用一对反引号``来表示字符串,称之为模板字符串。
1、模板字符串内部可以随意的回车、换行、加空格,均有效,不需要另外考虑字符串拼接的问题。
2、在模板字符串内部可以用${变量名}的方式来直接取变量的值,此时在模板字符串内部,变量与字符串之间不需要用连字符+号进行拼接。
var str1 = `ab ca`; var a = 'hello'; console.log(str1); console.log(`${a} world`); //ab ca // hello world
const和let var之后都可以被ES6语法当中的const和let所代替。
const:用const定义的变量,之后不允许再对其赋值(赋值也不起作用);
const a = 10;
a = 20;
console.log(a); // 10
如果const定义的是一个对象,该对象不允许再变,但该对象当中的某个属性所对应的属性值是允许再被改变赋值的。
const b={a:10};
console.log(b.a); // 10
b.a=20;
console.log(b.a); // 20
如果定义一个变量,该变量的值之后不会再发生变化,可以使用const, 一般我们把node当中载入的模块使用const定义的变量来接收,防止以后再对该变量进行赋值,使代码更加严谨。
let:使用let定义变量相当于定义了一个局部作用域变量,只会影响当前这个作用域。
如果定义一个变量,该变量的取值之后是会发生变化的,即用let来定义。避免使用var使得变量的污染域变大。
V8对于ES6的语法的支持情况分为三个级别:根本不支持、直接支持、严格模式下支持。对于const和箭头函数及模板字符串都是直接支持的,而let关键字为严格模式下支持。若直接使用会出现如下的报错信息。
'use strict';
let a=10;
a=20;
console.log(a); // 20
javascript中的作用域和闭包
任何js代码在执行前都有大量工作要做,如果只依靠引擎自己,其实很难做到,需要用到引擎的一个得力助手:作用域
作用域为引擎提供了环境内每一个标识符的位置信息,引擎依赖这些信息可以迅速查找到他们定义的位置。标识符的位置信息,是你在写代码时将标识符写在哪里来决定的,在词法分析阶段(编译的第一个阶段),这些标识符的位置信息就会以有序列表的形式保存到环境的scope属性中(也就是作用域),以供引擎使用。也可以简单理解为,作用域里面保存的信息,在你写代码的时候已经决定了,而且会一直保持这个作用域不变。
作用域的查找规则
1、查找标识符的过程会始终从当前作用域开始,然后逐级的向外层嵌套的作用域展开,知道找到标识符,或抵达最外层的作用域(全局作用域)为止,如果找不到标识符,通常会导致错误发生。(从里向外查找)
2、每个执行环境都可以进入到外层作用域中查找标识符,但不能进入到内层作用域中查找标识符。
闭包原理:
https://www.imooc.com/article/285474?block_id=tuijian_wz
1、southtree代码管理工具 gitlab代码管理平台的使用
2、阿里云云效平台使用rdc、工作任务分配管理、bug修改
3、电销后台管理系统
4、vue 组件化开发
5、vue filter过滤器、find函数、map、set
6、h5与app联调 (navigator 对象包含有关浏览器的信息)** app && h5混合开发
判断安卓和ios环境:isAndroid: navigator.userAgent.includes('Android'),
isIOS: navigator.userAgent.includes('OS')
安卓: 判断navigator.userAgent.includes('yangcheJS') 是否存在yangcheJS
存在则与app匹配成功 => 赋值:this.mobilePhone = JSON.parse(window.yangcheJS.getUserPhone())
h5向app传值: (均为app端定义的方法,h5则直接调用app方法传值)
let arrData = { payLink: this.body }
window.yangcheJS.WebViewJavascriptBridge('RequestPayment', JSON.stringify(arrData), '', 'onlinePay', '')
app回调h5的方法 将h5的方法暴露到window上
let me = this;
window['getStatus'] = (status) => {
me.getStatus(status);
}
getStatus()
IOS:
IOS:window.webkit.messageHandlers.getUserPhone.postMessage('111');先向ios发送一串指令(类似注册)(getUserPhone为约定方法)
后接收ios传过来的数据(同安卓)暴露一个getUserPhone()方法method里接收数据
7、vue图片前端签名直传阿里云oss
接口获取阿里云服务端 签名、policy、OSSAccessKeyId
const x2js = require('x2js');
let formData = new FormData()
// 参数顺序不能变
formData.append('key', '${filename}') // key默认${filename}
formData.append('policy', this.aliForm.policy)
formData.append('OSSAccessKeyId', this.aliForm.ossAccessKeyId)
formData.append('success_action_status', '201') // 默认201状态*
formData.append('Signature', this.aliForm.signature)
formData.append('file', file.raw)
this.$http({
method: 'post',
url: 'https://c1-yangche.oss-cn-hangzhou.aliyuncs.com',
data: formData
}).then((resd) => {
let myheadInfo = this.$x2js.xml2js(resd.data)
console.log(resd)
this.ruleForm.headImg = myheadInfo.PostResponse.Location
}).catch((err) => {
})
8、promise.all/async await 异步接口并发处理
9、encodeURI(string)对字符串编码,encodeURIComponent(string)对字符串解码(特殊字符:冒号、正斜杠、问号和井字号)
10、vue 引入百度地图获取定位 解决new BMap未定义 (高德地图就是个垃圾)
11、不是为了vue写的插件不支持Vue.use()加载方式 使用Vue.prototype挂载到vue原型上添加全局属性和方法
12、组件之间传值方法
父组件 =》 子组件
页面:<userManage-box @msg=“getMsg :msg=msg></userManage-box>
父组件定义 component:{"userManage-box": () => import("./userManage-box")} 传值 :msg=msg
子组件使用prop接收 接收值类型:Array String Number。。
子组件 =》 父组件
this.$emit(msg,getMsg) msg:方法名字,getMsg:要传出的值
父组件定义方法接收 @msg=“getMsg” method里getMsg(e)接收子组件传过来的值
兄弟组件之间通过父组件通信
13、vue webSocket 通信
https://www.jianshu.com/p/938004c22ed9
14、微信web-view(小程序内置h5)
h5端:引入微信jssdk/使用npm安装jssdk
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
npm install weixin-js-sdk --save
import wx from 'weixin-js-sdk';
Vue.prototype.$wx = wx;
h5向小程序传值:
mounted(){
wx.miniProgram.getEnv(res => {
this.inLiteApp = res.miniprogram
})
}
方法: {
if (this.inLiteApp == true) {
wx.miniProgram.navigateTo({
url: '/pages/payment/index/main?orderId=' + this.detail.orderId + '&orderCode=' + this.detail.orderCode + '&amount=' + this.detail.stats[2].value
})
}
}
小程序接收h5传过来的值(h5传过来的值直接从optios里取)
async onLoad(options) {
this.payInfo = options
}
小程序向h5传值:
<web-view :src="webviewUrl" @message="postMessage"/>
onLoad(options) {
// params为其他页面或者接口里接收到的数据
const params = {
userId: this.userId,
ticket: this.ticket,
appTicket: this.appTicket
}
this.webviewUrl = `https://mini.yangche51.com/#/home?parmas=${this.params}`;
}
h5接收小程序传过来的值
created() {
wx.miniProgram.getEnv(res => {
if (res.miniprogram) { //判断微信环境
const queryUrl = this.getRequest()
let urlParams = Object.assign(queryUrl, this.$route.query) //参数合并 Object.assign()是浅拷贝
let wxParmas = JSON.parse(queryUrl.parmas)
console.log('我接收到的参数', wxParmas)
document.cookie = "app_ticket=" + wxParmas.appTicket; //接收到的微信参数放入cookie里
this.$store.dispatch("auth/saveToken", { 调取store里的user.js的auth/saveToken方法,更新数据
token: wxParmas.ticket,
remember: 365
})
}
})
},
methods: {
getRequest() {
var url = decodeURIComponent(location.search); // 对参数解码获取到'#'后面的参数
var theRequest = new Object();
if (url.indexOf("?") != -1) { // 查找是否包含'?'
var str = url.substr(1); // 截取字符串
var strs = str.split("&"); // 分割字符串
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); // unescape()函数解码字符串
}
}
return theRequest; //返回结果
},
}
15、md5密码加密
let password = md5(this.password);默认小写32位
let password = md5(this.password).toUpperCase(); 大写32位
16、关于vue深拷贝与浅拷贝的思考
假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;
假设B复制了A,当修改A时,如果B没变,那就是深拷贝
深拷贝的实现
*递归复制所有层级属性
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)) {
// 判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
*JSON对象的parse和stringify实现
function deepClone(obj) {
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
17、vue操作数组的几种方法(push()、pop()、shift()和unshift()实现数组的增删操作、sort()和 reverse()对数组项进行排序操作)
concat():先给当前数组创建一个副本,然后将接收到的参数添加到这个副本(数组)的末尾,最后返回一个新的数组
var arr = [`大漠`,'30','W3cplus'];
console.log(arr); // ["大漠", "30", "W3cplus"]
var arr2 = arr.concat('Blog','2014');
console.log(arr2); // ["大漠", "30", "W3cplus", "Blog", "2014"]
**concat()方法是在数组的副本上进行操作并返回新构建的数组,所以并不会影响到原来的数组。
https://blog.csdn.net/qq_35192741/article/details/80655464
18、解构赋值
对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
19、Promise / vue中axios的封装
promise是异步编程的一种解决方案,比传统异步解决方案【回调函数】和【事件】更合理、更强大
安装:axios npm install axios
引入:在项目的src目录中新建一个request文件夹,然后在里面新建一个http.js和api.js文件。http.js文件用来封装axios,api.js用来统一管理接口
// 在http.js中引入axios
import axios from 'axios'; // 引入axios
import QS from 'qs'; // 引入qs模块,用来序列化post类型的数据,后面会提到
// vant的toast提示框组件,大家可根据自己的ui组件更改。
import { Toast } from 'vant';
环境的切换:项目环境中可能有开发、测试、生产环境,我们通过node的环境变量来匹配我们的默认接口url前缀。axios.defaults.baseURL可以设置axios的默认请求地址
// 环境的切换
// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = 'https://www.baidu.com';}
else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = 'https://www.ceshi.com';
}
else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'https://www.production.com';
}
设置请求超时:通过axios.default.timeout设置默认的请求超时时间。例如超过10s,就会告知用户当前接口请求超时,请刷新等
axios.default.timeout = 10000;
post请求头的设置:post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置,即设置post的请求头为application/x-www-form-urlencoded;charset=UTF-8
axios.default.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
请求拦截:有些请求是需要用户登录之后才能访问的,或者post请求时我们需要序列化我们提交的数据,这时候我们可以在请求的发送之前进行拦截,从而进行我们想要的操作
//先导入vuex,因为我们要使用到里面的状态对象
//vuex的路径根据自己的路径去写
import store from '@/store/index';
//请求拦截器
axios.interceptors.request.use(
config => {
// 每次发送请求之前判断vuex中是否存在token
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.state.token;
token && (config.headers.Authorization = token);
return config;
},
error => {
return Promise.error(error);
}
)
token:一般是在登录完成之后,将用户的token通过localStorage或者cookie存在本地,然后用户每次在进入页面的时候(main.js中),会首先从本地存储中读取token,如果token存在则说明用户已经登录过了,则更新vuex中的token状态。然后,在每次请求接口的时候都会在header中带上token,
后台就可以根据你带的token判断你的登录是否过期,如果没有携带,则说明没有登录过。
响应拦截:服务器返给我们的数据,我们在拿到之前根据后台返回的状态码对他进行一些处理(200 成功,401 未登录, 403 token过期, 404 请求不存在, 其他直接抛出错误提示)
//响应拦截器
axios.interceptors.response.use(
response => {
// 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
// 否则的话跑出错误
if(response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
//状态码不是2开头的情况
//这里可以跟你们后台开发人员协商好统一的错误码
//然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等
// 下面列举几个常见的操作,其他需求可自行扩展
error => {
if(error.response.status) {
switch (error.response.status) {
//401:未登录
//未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页面操作
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
break;
//403 token过期
// 登录过期对用户进行提醒
// 清除本地token和清空vuex中的token对象
// 跳转登录页面
case 403:
Toast({
message: '登录过期,请重新登录',
duration: 1000,
forbidClick: true
});
//清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
//跳转登录页面,并将要浏览的页面fullPath传过去,登陆成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
//404请求不存在
case 404:
Toast({
message: '网络请求不存在',
duration: 1500,
forbidClick: true
});
break;
//其他错误,直接抛出错误提示
default:
Toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
});
}
return Promise.reject(error.response);
}
});
封装get方法和post方法
20、vuex (vue状态管理器)
解释:一个专为vue应用程序开发的状态管理模式 它集中式存储管理应用的所有组件的状态,而更改状态的唯一方法就是提交mutation
例:this.$store.commit('SET_VIDEO_PAUSE', video_pause), SET_VIDEO_PAUSE为mutations属性中定义的方法
五种状态和属性:State、Getter、Mutation、Action、Module(mapAction)
流程:页面通过mapAction异步提交事件到action。action通过commit把对应的参数同步提交到mutation。mutation会修改state里对应的值。最后通过getter把对应的值跑出去,在页面计算属性中通过mapGetter来动态获取state里的值
State的特性:存放数据的地方,类似一个仓库
当mutation修改了state里的数据的时候,会动态修改所有调用这个变量的所有组件里的值(若是store中的数据发生变化,依赖这个数据的组件就会实时更新)
Getter的特性:getter用来获取数据,mapGetter经常在计算属性中被使用(通常放在mixins里全局使用)
Mutation的特性:Action类似于mutation,不同在于:
Action提交的是mutation,而不是直接变更状态
Action可以包含任意异步操作
优势:1、响应式的数据管理 一个页面发生数据变化,动态的改变对应的页面,相比使用localStorage只能存储字符串数据格式,因此还得封装自己的写入写出,localStorage优势在于永久存储
1、多个组件依赖于同一状态时。
2、来自不同组件的行为需要变更同一状态。
21、typeOf {} //object 用于返回数据的数据类型
22、webpack(模块打包机)
是什么:webpack是一个打包模块化js的工具,分析你的项目结构,找到js模块以及其他的一些浏览器不能直接运行的拓展语言(scss,ts等),并将其打包为合适的格式以供浏览器使用。
23、vue前端性能优化
1、vue-router路由懒加载(按需加载页面,提高页面加载性能,提高用户体验)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: resolve => require(['@/components/DefaultIndex'],resolve),
children: [
{
path: '',
component: resolve => require(['@/components/Index'],resolve)
},
{
path: '*',
redirect: '/Index'
}
]
})
2、webpack压缩图片(减少图片大小)
安装image-webpack-loader
npm install image-webpack-loader --save-dev
配置
在webpack.base.conf.js文件中引入配置
1: 引入:
require("image-webpack-loader")
2:配置:
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
},
或者也可配置为:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
}
3、取消额外打包的js文件
配置
在config/index.js文件里面修改
build:{}里面的productionSourceMap为false
4、打包后的js过大,将js打包多个文件
entry:{
main:'xxx.js'
}
plugins:{
new commonsChunkPlugin({
name:'commons',
minChunks:function(module){
// 下边return参考的vue-cli配置
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}) ,
// 以下才是关键
new commonsChunkPlugin({
name:'charts',
chunks:['commons']
minChunks:function(module){
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1
)
}
})
}
5、去掉不必要的插件
plugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({ warnings: false,
compress: {
join_vars: true,
warnings: false,
},
toplevel: false,
ie8: false,
]
6、gzip压缩
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";
7、服务器缓存
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
access_log off;
expires 30d;
}
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}
location ~* ^.+\.(html|htm)$ {
expires 1h;
}
8、遍历数组往每一项中添加一个字段
this.lists = res.body.map(item => {
this.$set(item, 'type', -1)
return item
})
1、vue-router路由懒加载(按需加载页面,提高页面加载性能,提高用户体验)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: resolve => require(['@/components/DefaultIndex'],resolve),
children: [
{
path: '',
component: resolve => require(['@/components/Index'],resolve)
},
{
path: '*',
redirect: '/Index'
}
]
})
2、webpack压缩图片(减少图片大小)
安装image-webpack-loader
npm install image-webpack-loader --save-dev
配置
在webpack.base.conf.js文件中引入配置
1: 引入:
require("image-webpack-loader")
2:配置:
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
},
或者也可配置为:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
}
3、取消额外打包的js文件
配置
在config/index.js文件里面修改
build:{}里面的productionSourceMap为false
4、打包后的js过大,将js打包多个文件
entry:{
main:'xxx.js'
}
plugins:{
new commonsChunkPlugin({
name:'commons',
minChunks:function(module){
// 下边return参考的vue-cli配置
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}) ,
// 以下才是关键
new commonsChunkPlugin({
name:'charts',
chunks:['commons']
minChunks:function(module){
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1
)
}
})
}
5、去掉不必要的插件
plugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({ warnings: false,
compress: {
join_vars: true,
warnings: false,
},
toplevel: false,
ie8: false,
]
6、gzip压缩
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";
7、服务器缓存
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
access_log off;
expires 30d;
}
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}
location ~* ^.+\.(html|htm)$ {
expires 1h;
}
8、遍历数组往每一项中添加一个字段
this.lists = res.body.map(item => {
this.$set(item, 'type', -1)
return item
})