首页
统计
墙纸
留言
Search
1
PVE8优化
19 阅读
2
mysql创建数据库
12 阅读
3
jenkins根据分支、文件夹打包
12 阅读
4
vue-cli注册全局方法
7 阅读
5
开心的加班
7 阅读
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
登录
/
注册
Search
标签搜索
vue+elementui
Cicaba
累计撰写
146
篇文章
累计收到
13
条评论
首页
栏目
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
页面
统计
墙纸
留言
搜索到
18
篇与
的结果
2020-09-28
vue3.0
性能优化diff算法优化: https://vue-next-template-explorer.netlify.app/Vue2中的虚拟dom是进行全量的对比Vue3新增了静态标记(PatchF1ag),在与上次虚拟节点进行对比时候,只对比带有patch flag的节点并且可以通过flag的信息得知当前节点要对比的具体内容hoistStatic 静态提升Vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染Vue3中对于不参与更新的元素,会做静态提升,只会被创建一 次,在渲染时直接复用即可cachellandlers 事件侦听器缓存+默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化但是因为是同一个函数,所以没有追踪变化,直接缓 存起来复用即可响应数据底层api的优化vue2Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。vue3Proxy可以劫持整个对象,并返回一个新的对象。Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。ssr渲染当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。当静态内容大到一 定量级时候,会用_ createStat icVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。新增api组合api setupsetup执行时机beforeCreate:表示组件刚刚被创建出来,组件的data和methods还没有初始化好setupCreated:表示组件刚刚被创建出来,并且组件的data和methods已经初始化好setup注意点-由于在执行setup函数的时候,还没有执行Created生命周期方法,所以在setup函数中,是无法使用data和methods.由于我们不能在setup函数中使用data和methods,所以Vue为了避免我们错误的使用,它直接将setup函 数中this修改成了undefinedreactivereactive是Vue3中提供的实现响应式数据的方法,在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的reactive注意点:reactive参数必须是对象(json/arr)-如果给reactive传递了其它对象(number),默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式vue3中文文档
2020年09月28日
7 阅读
0 评论
1 点赞
2019-08-21
history实现SPA
<!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>SPA</title> </head> <body> <div id="rotes"> <a href="index">index</a> <a href="login">login</a> </div> <div id="routerContent"></div> </body> <script type="text/javascript"> class Roter{ constructor(){ this.router={}; this.nowUrl=null; } renter(path,callback){ this.router[path] = (type)=>{ if(type==1){ history.replaceState({path},null,path); }else if(type==2){ history.pushState({path},null,path); } callback(); } } refresh(type){ this.router[this.nowUrl] && this.router[this.nowUrl](type); } init(){ window.addEventListener("load",(e)=>{ this.nowUrl = location.href.slice(location.href.indexOf("/",9)) }) window.addEventListener('popstate', (e) => { this.nowUrl = history.state.path; this.refresh(1) }); document.querySelectorAll('#rotes a').forEach((el)=>{ el.addEventListener('click',(e)=>{ e.preventDefault(); let href = e.target.href.slice(e.target.href.indexOf("/",9)); this.nowUrl = href; this.refresh(2); }) }) } } let router = new Roter(); router.init() router.renter('/index',()=>[ routerContent.innerHTML="index" ]) router.renter('/login',()=>[ routerContent.innerHTML="login" ]) </script> </html>往返缓存默认情况下,浏览器会缓存当前会话页面,这样当下一个页面点击后退按钮,或前一个页面点击前进按钮,浏览器便会从缓存中提取并加载此页面,这个特性被称为“往返缓存”。此缓存会保留页面数据、DOM和js状态,实际上是将整个页面完好无缺地保留往历史记录栈中添加记录:pushState(state, title, url)浏览器支持度: IE10+state: 一个 JS 对象(不大于640kB),主要用于在 popstate 事件中作为参数被获取。如果不需要这个对象,此处可以填nulltitle: 新页面的标题,部分浏览器(比如 Firefox )忽略此参数,因此一般为 nullurl: 新历史记录的地址,可为页面地址,也可为一个锚点值,新 url 必须与当前 url处于同一个域,否则将抛出异常,此参数若没有特别标注,会被设为当前文档 urlreplaceState(state, title, url)改变当前的历史记录history.state返回当前历史记录的 statepopstate定义:每当同一个文档的浏览历史(即 history 对象)出现变化时,就会触发 popstate 事件。注意:若仅仅调用 pushState 方法或 replaceState 方法,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用 back 、 forward 、 go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。总结一般场景下,hash 和 history 都可以,除非你更在意颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。另外,根据 Mozilla Develop Network 的介绍,调用 history.pushState() 相比于直接修改 hash,存在以下优势:pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 #后面的部分,因此只能设置与当前 URL 同文档的 URLpushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;pushState() 可额外设置 title 属性供后续使用。这么一看 history 模式充满了 happy,感觉完全可以替代 hash 模式,但其实 history 也不是样样都好,虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如http://www.qq.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如http://www.qq.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404错误。Vue-Router官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
2019年08月21日
4 阅读
1 评论
0 点赞
2019-04-09
vue-cli集成TypeScript
1- 安装ts-loader和typescriptnpm install --save-dev ts-loader@3 typescriptvue-cle@2.9使用的webpack为3, 所以必须使用ts-loader@32- 修改webpack.base.conf.js配置文件修改webpack.base.conf.js下的entry>app为'./src/main.ts' extensions: ['.js', '.vue', '.json', '.ts', '.tsx']//添加'.ts', '.tsx' 文件引入不用写后缀 rules: [ { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, options: { appendTsSuffixTo: [/\.vue$/], } },//添加tsloader加载器 ...其他 ]3- 在src目录下新建一个文件vue-shims.d.ts,用于识别单文件vue内的ts代码declare module "*.vue" { import Vue from "vue"; export default Vue; }4- 在项目根目录下建立TypeScript配置文件tsconfig.json{ "compilerOptions": { "strict": true, "module": "es2015", "moduleResolution": "node", "target": "es5", "allowSyntheticDefaultImports": true, "lib": [ "es2017", "dom" ] } } //"jsx": "preserve" jsx支持5- 修改main.js后缀改为ts, 添加引入文件的的后缀.6- 修改router.js后缀改为ts.7- 测试//下面可以测试是否集成成功,编辑src/components/Hello.vue文件,修改 <script lang="ts"> import Vue, {ComponentOptions} from 'vue' export default { name: 'hello', data() { return { msg: 'this is a typescript project now' } } } as ComponentOptions<Vue> </script>8- 配置官方推荐的,vue-class-component修改ts配置文件,增加以下两项配置"allowSyntheticDefaultImports": true,"experimentalDecorators": true,使用vue-class-component后,初始数据可以直接声明为实例的属性,而不需放入data() {return{}}中,组件方法也可以直接声明为实例的方法,如官方实例,更多使用方法可以参考其官方文档import Vue from 'vue' import Component from 'vue-class-component' // @Component 修饰符注明了此类为一个 Vue 组件 @Component({ // 所有的组件选项都可以放在这里 template: '<button @click="onClick">Click!</button>' }) export default class MyComponent extends Vue { // 初始数据可以直接声明为实例的属性 message: string = 'Hello!' // 组件方法也可以直接声明为实例的方法 onClick (): void { window.alert(this.message) } }问题1- ts 无法识别 requireyarn add @types/webpack-env -D
2019年04月09日
2 阅读
0 评论
0 点赞
2018-04-24
在已有的Vue项目添加单元测试
1.在相对应的文件夹添加一下3个文件.2.安装对应的依赖npm i -D karma karma-webpack phantomjs-prebuilt karma-phantomjs-launcher karma-phantomjs-shim karma-chrome-launcher karma-sourcemap-loader mocha karma-mocha sinon chai sinon-chai karma-sinon-chai karma-spec-reporter karma-coverage运行npm run unit你会看到一下错误信息{ "message": "Error: [vuex] vuex requires a Promise polyfill in this browser.\nat webpack:///~/vuex/dist/vuex.esm.js:97:19 <- index.js:11802:55", "str": "Error: [vuex] vuex requires a Promise polyfill in this browser.\nat webpack:///~/vuex/dist/vuex.esm.js:97:19 <- index.js:11802:55" } //使用Babel polyfill解决了这个问题。以下是我所做的步骤: //安装Babel Polyfill: npm install --save-dev babel-polyfill //然后包括前内源和测试文件的填充工具文件files的部分karma.conf.js: files: [ '../node_modules/babel-polyfill/dist/polyfill.js', 'index.js' ],注意异步主件在断言时必须保证渲染完成.所以要使用nextTick
2018年04月24日
2 阅读
0 评论
0 点赞
2018-04-18
固定浏览器ULR
vm.$watch('$route', (now) => { window.history.pushState(null, null, window.location.origin);});
2018年04月18日
3 阅读
0 评论
0 点赞
2018-04-18
Vuex全局守卫
全局守卫你可以使用 router.beforeEach 注册一个全局前置守卫:const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ... })每个守卫方法接收三个参数:to: Route: 即将要进入的目标 路由对象from: Route: 当前导航正要离开的路由next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。利用router.beforeEach禁止跳转不存在的路由router.beforeEach((to, from, next) => { let routes = router.options.routes; //递归判断是否跳转路由 let jump = (arr, to, from, next) => { for (let v of arr) { if (v.path === to.path) { next(); continue; } else { if (v.children instanceof Array && v.children.length > 0) { jump(v.children, to, from, next); } next(false); } } }; jump(routes, to, from, next); });
2018年04月18日
1 阅读
0 评论
0 点赞
2018-04-17
Vue-router向路由主件传参
通过配置路由的props进行传参如果 props 被设置为 true,route.params 将会被设置为组件属性。const User = { props: ['id'], template: '<div>User {{ id }}</div>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, props: true } ] })如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。const router = new VueRouter({ routes: [ { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } } //主件的props:['newsletterPopup'] ] })你可以创建一个函数返回 props。const router = new VueRouter({ routes: [ { path: '/search', component: SearchUser, props: (route) => ({ query: route }) } //主件的props:['query'] ] })
2018年04月17日
1 阅读
0 评论
0 点赞
2018-04-03
JS计算精度问题
export default function point(f) { if (f === '+') { return accAdd; } else if (f === '-') { return accSub; } else if (f === '*') { return accMul; } else { return accDiv; } } /** ** 除法函数,用来得到精确的除法结果 ** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 ** 调用:accDiv(arg1,arg2) ** 返回值:arg1除以arg2的精确结果 **/ function accDiv(arg1, arg2) { var t1 = 0; var t2 = 0; var r1, r2; try { t1 = arg1.toString().split('.')[1].length; } catch (e) {} try { t2 = arg2.toString().split('.')[1].length; } catch (e) {} r1 = Number(arg1.toString().replace('.', '')); r2 = Number(arg2.toString().replace('.', '')); return (r1 / r2) * Math.pow(10, t2 - t1); } //给Number类型增加一个div方法,调用起来更加方便。Number.prototype.div = function (arg) { return accDiv(this, arg); /** ** 乘法函数,用来得到精确的乘法结果 ** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 ** 调用:accMul(arg1,arg2) ** 返回值:arg1乘以 arg2的精确结果 **/ function accMul(arg1, arg2) { var m = 0; var s1 = arg1.toString(); var s2 = arg2.toString(); try { m += s1.split('.')[1].length; } catch (e) {} try { m += s2.split('.')[1].length; } catch (e) {} return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m); } // 给Number类型增加一个mul方法,调用起来更加方便。Number.prototype.mul = function (arg) { return accMul(arg, this); /** ** 减法函数,用来得到精确的减法结果 ** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。 ** 调用:accSub(arg1,arg2) ** 返回值:arg1加上arg2的精确结果 **/ function accSub(arg1, arg2) { var r1, r2, m, n; try { r1 = arg1.toString().split('.')[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split('.')[1].length; } catch (e) { r2 = 0; } m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度 n = (r1 >= r2) ? r1 : r2; return ((arg1 * m - arg2 * m) / m).toFixed(n); } // 给Number类型增加一个mul方法,调用起来更加方便。Number.prototype.sub = function (arg) { return accMul(arg, this); /** ** 加法函数,用来得到精确的加法结果 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。 ** 调用:accAdd(arg1,arg2) ** 返回值:arg1加上arg2的精确结果 **/ function accAdd(arg1, arg2) { var r1, r2, m, c; try { r1 = arg1.toString().split('.')[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split('.')[1].length; } catch (e) { r2 = 0; } c = Math.abs(r1 - r2); m = Math.pow(10, Math.max(r1, r2)); if (c > 0) { var cm = Math.pow(10, c); if (r1 > r2) { arg1 = Number(arg1.toString().replace('.', '')); arg2 = Number(arg2.toString().replace('.', '')) * cm; } else { arg1 = Number(arg1.toString().replace('.', '')) * cm; arg2 = Number(arg2.toString().replace('.', '')); } } else { arg1 = Number(arg1.toString().replace('.', '')); arg2 = Number(arg2.toString().replace('.', '')); } return (arg1 + arg2) / m; } //给Number类型增加一个add方法,调用起来更加方便。Number.prototype.add = function (arg) { return accAdd(arg, this);
2018年04月03日
0 阅读
0 评论
0 点赞
2018-03-16
vue-cli注册全局方法
把方法挂载到window对象上!import axios from 'axios'; window.axios = axios;//这样每个vue主件都可以调用axios把方法挂载到Vue的原型上import Vue from 'vue'; import axios from 'axios'; Vue.prototype.axios = axios;通过webpack全局注入!plugins:[ new webpack.ProvidePlugin({ //webpack配置项 axios:"axios", jQuery: "jquery", $: "jquery" }) ]总结:挂载到window上将被暴露在全局,(不推荐使用).挂载到Vue原型上, 可以在每个Vue实例上使用.(每次使用都的加this).webpack注入, 会在编译后在每个模块中引入.(推荐使用)
2018年03月16日
7 阅读
0 评论
0 点赞
2018-02-22
axios 拦截 , 登陆验证, token 验证
第一步: 路由 多添加一个自定义字段 requireAuth path: '/repository', name: 'repository', meta: { requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的 }, component: Repository第二步:router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { // 判断该路由是否需要登录权限 if (store.state.token) { // 通过vuex state获取当前的token是否存在 next(); } else { next({ path: '/login', query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 }) } } else { next(); }登录拦截到这里就结束了吗?并没有。这种方式只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。(可手动在浏览器地址栏输入没有权限的路由)还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。第三步: 拦截器 (要想统一处理所有http请求和响应,就得用上 axios 的拦截器。)每次跳页面, 都要获取新路由对应的html页面, 这时候可以用axios的http拦截每次路由跳转, 都先让后台验证一下token是否有效, 在http头添加token, 当后端接口返回 401 Unauthorized(未授权) ,让用户重新登录。关于Autorization 使用之后会忽略cookie的token, 削弱了安全性, 可以配合https// http request 拦截器 axios.interceptors.request.use( config => { if (store.state.token) { // 判断是否存在token,如果存在的话,则每个http header都加上token config.headers.Authorization = `token ${store.state.token}`; } return config; }, err => { return Promise.reject(err); }); // http response 拦截器 axios.interceptors.response.use( response => { return response; }, error => { if (error.response) { switch (error.response.status) { case 401: 401 旌旗 灵医 , 只用[授权] 旌旗的医生 才是 灵医 // 返回 401 清除token信息并跳转到登录页面 store.commit(types.LOGOUT); router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }) } } return Promise.reject(error.response.data) // 返回接口返回的错误信息 }); 完整的方法见 /src/http.js .通过上面这几步,就可以在前端实现登录拦截了。 登出 功能也就很简单,只需要把当前token清除,再跳转到首页即可。githubimport axios from 'axios' import Qs from 'qs'; import 'babel-polyfill'; import router from './router'; import { successMessage, warningMessage, errorMessage } from '@/util/message.js'; const handleErrorResponse = (response, config) => { if (response) { switch (response.status) { case 401: // 未登录或过期,跳至登录页 if (router.currentRoute.path !== '/login') { router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); } break; default: if(config && config.silent === true) break; if (response.data && response.data.success === false) errorMessage(response.data.message.substr(0, 60)); else errorMessage('服务器发生 ' + response.status + ' 错误.'); // errorMessage('服务器发生 ' + error.response.status + ' 错误.'); break; } } else if (!config || config.silent !== true) { errorMessage("服务器连接失败"); } }; // http response 拦截器 axios.interceptors.response.use( response => { return response; }, error => { handleErrorResponse(error.response, error.config); return Promise.reject(error) // 返回接口返回的错误信息 }); // ajax 全局错误处理 (function($){ $.ajaxSetup && $.ajaxSetup({ error: function(response) { handleErrorResponse(response.status && response, this); } }); })(jQuery); export default axios;
2018年02月22日
1 阅读
0 评论
0 点赞
1
2