关于vue.js:vue组件通信方式有哪些

vue组件通信形式一、props(父向子传值----自定义属性) / $emit(子向父传值----- 自定义事件)父组件通过props的形式向子组件传递数据,而通过$emit 子组件能够向父组件通信。 1. 父组件向子组件传值(props)上面通过一个例子阐明父组件如何向子组件传递数据:在子组件article.vue中如何获取父组件section.vue中的数据articles:['红楼梦', '西游记','三国演义'] // section父组件<template> <div class="section"> <com-article :articles="articleList"></com-article> </div></template><script>import comArticle from './test/article.vue'export default { name: 'HelloWorld', components: { comArticle }, data() { return { articleList: ['红楼梦', '西游记', '三国演义'] } }}</script>// 子组件 article.vue<template> <div> <span v-for="(item, index) in articles" :key="index">{{item}}</span> </div></template><script>export default { props: ['articles']}</script>留神: prop 能够从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被批改,强行批改能失效,然而控制台会有错误信息。 在子组件批改父组件传入的值的办法:1 .sync 父组件v-on绑定自定义属性时增加修饰符.sync 在子组件中通过调用emit(′update:自定义属性′,要批改的新值)==>emit('update:自定义属性',新值) 固定写法 此时子组件中接管的值就更新成了新值(父组件中的原始值会跟着变动,控制台不会报错) 父组件中: <child :value.sync='xxx'/> 子组件中: this.$emit('update:value',yyy) 2.在子组件data中申明本人的数据,让接管的数据作为这个数据的值 ==> 子组件的数据=this.value (这种办法理论批改的是本人的数据 父组件的数据没变) ...

October 12, 2022 · 4 min · jiezi

关于vue.js:假如问你是怎样优化Vue项目的该怎么回答

咱们在开发Vue我的项目时候都晓得,在vue开发中某些问题如果后期疏忽掉,过后不会呈现显著的成果,然而越向后开发越难做,而且我的项目做久了就会呈现问题,这就是所说的蝴蝶效应,这样前期的保护老本会十分高,并且我的项目上线后还会影响用户体验,也会呈现加载慢等一系列的性能问题,上面举一个简略的例子。 举个简略的例子如果加载我的项目的时候加载一张图片须要0.1s,其实算不了什么能够忽略不计,然而如果我有20张图片,这就是2s的工夫, 2s的工夫不算长一下就过来了,然而这仅仅的只是加载了图片,还有咱们的js,css都须要加载,那就须要更长的工夫,可能是5s,6s...,比方加载工夫是5s,用户可能等都不会等,间接敞开咱们的网站,最初导致咱们网站流量很少,流量少就没人用,没人用就没有钱,没有钱就涨不了工资,涨不了工资最初就是跑路了。通过下面的例子能够看出性能问题是如许的重要甚至关系到了咱们薪资,那如何防止这些问题呢?废话不多说,上面分享一下本人在写我的项目的时用到的一些优化计划以及注意事项。 1.不要将所有的数据都放在data中能够将一些不被视图渲染的数据申明到实例内部而后在外部援用援用,因为Vue2初始化数据的时候会将data中的所有属性遍历通过Object.definePrototype从新定义所有属性;Vue3是通过Proxy去对数据包装,外部也会波及到递归遍历,在属性比拟多的状况下很消耗性能 <template> <button @click="updateValue">{{msg}}</button></template><script>let keys=true;export default { name:'Vueinput', data(){ return { msg:'true' } }, created(){ this.text = 'text' }, methods:{ updateValue(){ keys = !keys this.msg = keys?'true':'false' } }}</script> 2.watch 尽量不要应用deep:true深层遍历因为watch不存在缓存,是指定监听对象,如果deep:true,并且监听对象类型状况下,会递归解决收集依赖,最初触发更新回调3. vue 在 v-for 时给每项元素绑定事件须要用事件代理vue源码中是通过addEventLisener去给dom绑定事件的,比方咱们应用v-for须要渲染100条数据并且并为每个节点增加点击事件,如果每个都绑定事件那就存在很多的addEventLisener,这里不用说性能上必定不好,那咱们就须要应用事件代理解决这个问题<template> <ul @click="EventAgent"> <li v-for="(item) in mArr" :key="item.id" :data-set="item">{{item.day}}</li> </ul></template><script>let keys=true;export default { name:'Vueinput', data(){ return { mArr:[{ day:1, id:'xx1' },{ day:2, id:'xx2' },{ day:2, id:'xx2' }, ... ] } }, methods:{ EventAgent(e){ // 留神这里 在我的项目中千万不要写的这么简略,我只是为了不便了解才这么写的 console.log(e.target.getAttribute('data-set')) } }}</script>4. v-for尽量不要与v-if一起应用vue的编译过程是template->vnode,看上面的例子// 假如data中存在一个arr数组<div id="container"> <div v-for="(item,index) in arr" v-if="arr.length" key="item.id">{{item}}</div></div>下面的例子有可能大家常常这么做,其实这么做也能达到成果然而在性能下面不是很好,因为Ast在转化为render函数的时候会将每个遍历生成的对象都会退出if判断,最初在渲染的时候每次都每个遍历对象都会判断一次须要不须要渲染,这样就很节约性能,为了防止这个问题咱们把代码略微改一下<div id="container" v-if="arr.length"> <div v-for="(item,index) in arr" >{{item}}</div></div>这样就只判断一次就能达到渲染成果了,是不是更好一些那参考 前端vue面试题具体解答 ...

October 12, 2022 · 3 min · jiezi

关于vue.js:vue面试考察知识点全梳理

一、简介vue几个核心思想: 数据驱动组件化虚构dom、diff部分最优更新源码目录介绍Vue.js 的源码在 src 目录下,其目录构造如下。 src├── compiler # 编译相干 ├── core # 外围代码 ├── platforms # 不同平台的反对├── server # 服务端渲染├── sfc # .vue 文件解析├── shared # 共享代码compiler:编译相干的代码。它包含把模板解析成 ast 语法树,ast 语法树优化,代码生成等性能。core:外围代码,包含内置组件、全局 API 封装,Vue 实例化、观察者、虚构 DOM、工具函数等等。platform:不同平台的反对,是 Vue.js 的入口,2 个目录代表 2 个次要入口,别离打包成运行在 web 上和 weex 上的 Vue.js。server:服务端渲染,把组件渲染为服务器端的 HTML 字符串,将它们间接发送到浏览器,最初将动态标记"混合"为客户端上齐全交互的应用程序。sfc: .vue 文件内容解析成一个 JavaScript 的对象。shared:浏览器端和服务端所共享的工具办法。源码构建基于 Rollup 构建,相干配置在 scripts 目录下。 构建时通过不同的命令执行不同的脚本,去读取不同用途的配置,而后生成适宜各种场景的Vue源码。 vue2.0有以下几种场景: 浏览器端服务端渲染配合weex平台在客户端应用类型查看在vue2.x版本中应用 Flow 作为js动态类型查看工具,3.x版本应用typescript实现,自带类型查看。 二、数据驱动vue核心思想之一就是数据驱动,指数据驱动生成视图,通过批改数据主动实现对视图的批改。这里次要剖析模板和数据是如何渲染成最终的DOM的。 1. new Vue的过程Vue 初始化次要就干了几件事件, 合并配置初始化生命周期初始化事件核心初始化渲染初始化 data、props、computed、watcher 等等。2. 实例挂载$mount办法 Vue 不能挂载在 body、html 这样的根节点上;如果没有定义 render 办法,则会把 el 或者 template 字符串转换成 render 办法在 Vue 2.0 版本中所有 Vue 的组件的渲染最终都须要 render 办法,是一个“在线编译”的过程;挂载组件: mountComponent外围就是先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 办法,在此办法中调用 vm._render 办法学生成虚构 Node,最终调用 vm._update 更新 DOM。 ...

October 12, 2022 · 5 min · jiezi

关于vue.js:vue这些原理你都知道吗面试版

前言在之前面试的时候我本人也常常会遇到一些vue原理的问题, 我也总结了下本人的常常的用到的,不便本人学习,明天也给大家分享进去, 欢送大家一起学习交换, 有更好的办法欢送评论区指出, 后序我也将继续整顿总结~ 形容 Vue 与 React 区别阐明概念:vue:是一套用于构建用户界面的渐进式框架,Vue 的外围库只关注视图层react:用于构建用户界面的 JavaScript 库 申明式, 组件化 定位vue 渐进式 响应式React 单向数据流写法 vue:template,jsx react: jsxHooks:vue3 和 react16 反对 hookUI 更新文化vue 官网提供React 第三方提供,本人抉择整个 new Vue 阶段做了什么?vue.prototype._init(option)initState(vm)Observer(vm.data)new Observer(data)调用 walk 办法,遍历 data 中的每个属性,监听数据的变动执行 defineProperty 监听数据读取和设置数据描述符绑定实现后,咱们就能失去以下的流程图 [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-Dq3kKRrB-1665551088442)(https://p3-juejin.byteimg.com...)] 图中咱们能够看出,vue 初始化的时候,进行了数据的 get\set 绑定,并创立了一个dep 对象就是用来依赖收集, 他实现了一个公布订阅模式,完后了数据 data 的渲染视图 watcher 的订阅class Dep { // 依据 ts 类型提醒,咱们能够得出 Dep.target 是一个 Watcher 类型。 static target: ?Watcher; // subs 寄存收集到的 Watcher 对象汇合 subs: Array<Watcher>; constructor() { this.subs = []; } addSub(sub: Watcher) { // 收集所有应用到这个 data 的 Watcher 对象。 this.subs.push(sub); } depend() { if (Dep.target) { // 收集依赖,最终会调用下面的 addSub 办法 Dep.target.addDep(this); } } notify() { const subs = this.subs.slice(); for (let i = 0, l = subs.length; i < l; i++) { // 调用对应的 Watcher,更新视图 subs[i].update(); } }}形容 vue 的响应式原理[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-D2L76LQa-1665551088445)(https://p1-juejin.byteimg.com...)] ...

October 12, 2022 · 6 min · jiezi

关于vue.js:P1vue3-安装-简单的前端实例

1.装置node 环境 C02GQ39QQ05P:~ root# node -vv14.17.1C02GQ39QQ05P:~ root# npm install vueC02GQ39QQ05P:~ root# npm install -g vue-cli C02GQ39QQ05P:~ root# npm install webpack -g webstorm创立vue我的项目时能够获取环境变量时,即为上述装置胜利 编辑器中webstorm和 vs code 都是支流的 2.间接用 <script> 引入下载vue.js use CDN#dev<script src="https://unpkg.com/[email protected]"></script>#pro<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.33/vue.cjs.js"></script> 3.vue3 实例1). 创立div标签 2). 援用vue办法mount <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <script src="https://unpkg.com/[email protected]"></script></head><body><div id="app"></div><div id="counter56"> <p>{{ test56 }}</p> <p> test57 </p></div><script> const Counter = { data:function () { return { test56: "sssss", test57: "sean" } }, } Vue.createApp(Counter).mount('#counter56')</script></body></html>

October 12, 2022 · 1 min · jiezi

关于vue.js:Vue-Element-Admin-设置顶部导航并隐藏侧边栏

Vue Element Admin 可能是应用最宽泛的后盾治理前端框架了,尽管技术栈曾经老了(Vue 2.0 全家桶 + npm8),但社区仍旧沉闷。 目前有个我的项目,须要把侧边栏导航换成顶导,框架官网是不反对的,调研当前,决定本人写一个顶导,暗藏现有的侧边栏。 减少顶导菜单在 src/layout/components下,新建一个文件 TopMenu.vue: <template> <div class="topmenu"> <el-row> <el-col :span="8" class="logo-container"> <span class="title">ViaBTC 后盾管理系统</span> </el-col> <el-col :span="16" class="menu"> <el-menu :default-active="activeIndex2" class="el-menu-demo" mode="horizontal" background-color="#324057" text-color="#fff" active-text-color="#ffd04b" @select="handleSelect" > <el-menu-item index="1">解决核心</el-menu-item> <el-submenu index="2"> <template slot="title">我的工作台</template> <el-menu-item index="2-1"> <a href="#/components/split-pane">分割线</a> </el-menu-item> <el-menu-item index="2-2">选项2</el-menu-item> <el-menu-item index="2-3">选项3</el-menu-item> <el-submenu index="2-4"> <template slot="title">选项4</template> <el-menu-item index="2-4-1">选项1</el-menu-item> <el-menu-item index="2-4-2">选项2</el-menu-item> <el-menu-item index="2-4-3">选项3</el-menu-item> </el-submenu> </el-submenu> <el-menu-item index="3" disabled>音讯核心</el-menu-item> <el-menu-item index="4"> <a href="https://www.ele.me" target="_blank">订单治理</a> </el-menu-item> </el-menu> </el-col> </el-row> </div></template><script>export default { components: { }, data() { return { activeIndex: '1', activeIndex2: '1' } }, computed: { }, methods: { handleSelect(key, keyPath) { console.log(key, keyPath) console.log(this.permission_routes) }, }}</script><style lang="scss" scoped>.topmenu { width: 100%; height: 62px; min-width: 600px; background: #324057; color: #fff; border-bottom: 1px solid #1f2d3d; .logo-container { line-height: 60px; min-width: 200px; .logo { height: 50px; width: 50px; margin-right: 5px; vertical-align: middle; display: inline-block; } .title { vertical-align: middle; font-size: 18px; font-family: "Microsoft YaHei"; letter-spacing: 3px; } } .menu { .el-submenu { // 这个用于放弃一级菜单之间的间距 padding: 0 20px; } }}</style>在 src/layout/components/index.js中,import TopMenu: ...

October 11, 2022 · 2 min · jiezi

关于vue.js:玩转DIY可视化打造UniApp小程序

[]()1.1DIY可视化DIYGW可视化工具是一个傻瓜式、拖拽式、模块化开发软件工具;一个从想法到原型到源码,一步到位低代码生成源码工具。 所见即所得,拖拽设计,无编程根底的人都能轻松把握。只需一次设计,就能领有微信小程序、支付宝小程序、头条小程序、QQ小程序、百度小程序、FinClip小程序、H5、WebApp、UNIAPP等源码。 丰盛的组件模块,宫格列表、图文菜单、卡片、列表、轮播图、导航栏、按钮、标签、表单、单选、复选、下拉抉择、多层抉择、级联抉择、开关、时间轴、模态框、步骤条、头像、进度条、动静告诉栏、进度条、星级评分等。 丰盛的前后台通信模块,按钮点击事件、自定义办法调用、API在线调试、数据动静绑定、For循环数据绑定、IF判断绑定等 无论有无编程根底,都能在DIYGW.COM中找到本人的乐趣。 []()1.2uni-app介绍**uni-app 是一个应用 Vue.js开发所有前端利用的框架,开发者编写一套代码,可公布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快利用等多个平台。 DCloud公司领有900万开发者、数百万利用、12亿手机端月活用户、数千款uni-app插件、70+微信/qq群,腾讯课堂官网为uni-app录制培训课程,开发者能够释怀抉择。 uni-app在手,做啥都不愁。即便不跨端,uni-app也是更好的小程序开发框架、更好的App跨平台框架、更不便的H5开发框架。不论领导安顿什么样的我的项目,你都能够疾速交付,不须要转换开发思维、不须要更改开发习惯。 []()1.3小结不论你是产品经理、需要原型设计师、UI设计师、美工、前端页面工程师、前端开发人员、计算机老师还是学生,DIY官网可视化设计器都能够帮忙你轻松创立业余的、举世无双的利用。 高效的设计疾速生成源码,让你都能轻松做出私有化部署的利用APP。 帮忙业务专家和开发人员就利用需要和界面进行合作做出更灵便的更高效的客户体验应用程序,放慢上市速度并放慢利用交付速度,同时独特构建满足业务对性能和可用性的所有需要的利用,升高了老本,最大水平地缩小了低廉的资源。 []()第二章 **创立与公布[]()2.1设计器介绍设计器能力相似于sketch,figma的设计工具,但DIY可视化的劣势是可一键将设计原型间接变成源码。 2.1.1快捷功能区软件的顶部把一些罕用性能的性能放在顶部、及左边操作按钮区,鼠标挪动至对应的图标会有相干性能提醒,次要顶部工具栏:可撤销、重置、在线预览、真机预览、导出源码、保留、清空当前页代码;左边工具栏:高低挪动组件、复制组件、保留组件库、锁定图层、API及自定义办法、组件图层面板。 顶部:工具栏,可撤销、重置、在线预览、真机预览、导出源码、保留、清空当前页代码;利用级设置:全局变量、拦截器、第三方字体图标。 2.1.2页面治理页面治理位于软件的左上角地位,它的作用是治理整个利用的所有页面,也可能进行页面的增加、删除、命名、利落页面排序等操作。 当咱们单击页面名称的时候,这个页面会在主编辑区关上,新建页面默认只有题目,设计内容区是空白的,页面的内容是咱们本人来设计的。 2.1.3组件属性与款式组件的属性并不是每个组件一样,它们根底属性雷同,也有本身属性、通过属性设置来达到组件不同的成果、组件事件设置集成在属性设置里。 组件款式全副雷同,按组件的整体性来设置,不同的组件款式能够间接通过复制款式的形式反对拷贝至另一组件。 2.1.4利用快捷键零碎罕用的快捷键如下: 疾速复制:CTRL+C 跨页面复制:ALT+C 跨页面粘贴:CTRL+V 上移一层:↑ 下移一层:↓ 置顶:CTRL+ ↑ 置底:CTRL+ ↓ 重做:CTRL + Y 撤回:CTRL + Z 组件删除:CTRL+D或DEL 复制款式:CTRL +?F 粘贴款式:CTRL +?SHIFT + F 革除款式:CTRL +?SHIFT + L 保留:CTRL + S 点击组件右键,也会提醒相应的快捷键 []()2.2创立利用会员中心、点击新建新版UNIAPP、微信、支付宝、头条、百度等小程序、H5利用,创立实现后,点击编辑利用,进入设计器 []()2.3创立页面用户在左侧设计器,页面治理找到新增页面图标,点击后弹出输出页面标识、页面题目即可创立新的页面。 其中页面标识相当咱们生成代码的门路,比方:index/user/edit,示意index/user是目录级,最初的edit是页面标识,最好的一个标识将会生成一个.vue后缀名的文件。页面门路及页面名称零碎会在导出源码里主动生成在pages.json配置文件里。 2.3.1首页设置首页面对应所有页面的第一个页面,如果在设计的过程中,即可拖动页面进行页面排序。 2.3.2页面属性设置属性治理里,能够进行设置页面导航背景、导航字体色彩,页面背景图片色彩、整体页面字体大小、页面字体色彩等。 2.3.3根本组件设计页面点击左侧根本组件,外面蕴含了罕用的根底组件、容器组件、表单组件、图表组件,只须要手动选中的组件进设计区,而后在属性里批改组件对应的属性设计你想要的成果。 比方我拖动一个文本内容组件,抉择组件按住鼠标左键拖进设计区,左边属性能够对组件属性、组件款式进行针对性设置,所见即所得的设置。 2.3.4组件模板设计页面零碎内置了一系列罕用组件模板,感觉哪个模板适宜本人,只须要在下面点击一下,整个组件模板将在设计区减少此模板,能够拖动整个组件模板进行程序调整,同样能够通过左边属性能够对组件属性、组件款式进行针对性设置。 2.3.5页面模板设计页面页面模板设计页面相当页面级模板复用,零碎内置了一系列罕用页面模板,感觉哪个模板适宜本人,只须要在下面点击一下,整个页面将替换成该页面模板。 []()2.4全局设置全局设置次要是对利用级底部菜单的设置,设置底部菜单默认图标、选中图标、菜单题目、页面链接地址、默认题目色彩及选中题目色彩。如果用户设置后,代码最终会生成pages.json下的tabBar相干属性。不设置不生成相干底部菜单配置。 []()2.5查看代码点击工具栏区,查看源码,即可查看当前页的代码,用户能够疾速复制当前页的代码进已有的我的项目进行替换或新建页面进行开发。 ...

October 10, 2022 · 1 min · jiezi

关于vue.js:解读Vue3模板编译优化

明天的文章打算学习下 Vue3 下的模板编译与 Vue2 下的差别,以及 VDOM 下 Diff 算法的优化。 编译入口理解过 Vue3 的同学必定晓得 Vue3 引入了新的组合 Api,在组件 mount 阶段会调用 setup 办法,之后会判断 render 办法是否存在,如果不存在会调用 compile 办法将 template 转化为 render。 // packages/runtime-core/src/renderer.tsconst mountComponent = (initialVNode, container) => { const instance = ( initialVNode.component = createComponentInstance( // ...params ) ) // 调用 setup setupComponent(instance)}// packages/runtime-core/src/component.tslet compileexport function registerRuntimeCompiler(_compile) { compile = _compile}export function setupComponent(instance) { const Component = instance.type const { setup } = Component if (setup) { // ...调用 setup } if (compile && Component.template && !Component.render) { // 如果没有 render 办法 // 调用 compile 将 template 转为 render 办法 Component.render = compile(Component.template, {...}) }}这部分都是 runtime-core 中的代码,之前的文章有讲过 Vue 分为完整版和 runtime 版本。如果应用 vue-loader 解决 .vue 文件,个别都会将 .vue 文件中的 template 间接解决成 render 办法。 ...

October 10, 2022 · 5 min · jiezi

关于vue.js:vue的几个提效技巧

1.动静组件 <component :is='组件名'></component>联合v-for循环应用应用环境 如图,这是一个v-for渲染的列表(只是目前这个版块才刚开始做,目前只有一个),圆圈内的就是一个组件,也就是要v-for动静组件 [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-CgstVJ6d-1665390342414)(https://p3-juejin.byteimg.com...)] 理论应用一开始就是根本的组件引入了 import ColorIn from '@/components/Magic/ColorIn.vue'import LineIn from "@/components/Magic/LineIn.vue";import LineIn from "@/components/Magic/Header.vue";import LineIn from "@/components/Magic/Footer.vue";export default{ components:{ ColorIn, LineIn, Header, Footer }}接下来就是动静v-for动静组件的应用,componentList:['ColorIn','LineIn','Header','Footer']应用上面的代码即可将代码顺次循环 <component v-for="(item,index) in componentList" :key="index" :is="item"></component>编译当前的成果就是 <ColorIn></ColorIn><LineIn></LineIn><Header></Header><Footer></Footer>2.watch进阶应用立刻执行应用环境例如场景为页面一进来就调用拉取列表数据getList(),而后监听路由的$route.query.id而后触发列表数据的更新 理论应用为了让它一开始就执行,咱们须要在created()生命周期中执行一次拉取数据的办法 watch:{ '$route.query.id':{ handle(){ this.getList(); }, }},created(){ this.getList();},然而应用immediate即可立刻执行,改写当前的代码如下 watch:{ '$route.query.id':{ handle(){ this.getList(); }, immediate:true }},深度监听应用环境在监听对象的时候,对象的外部属性发生变化watch无奈监听到,这种时候就须要应用深度监听 理论应用只须要设置deep:true即可开启深度监听 data(){ return{ queryList:{ count:0, name:'', } }},watch:{ queryList:{ handle(newValue,oldValue){ //do something }, deep:true }},计算属性之setter理论应用咱们个别平时应用的都是getter,但其实它还有个setter,当计算属性的fullName触发更新的时候,就会触发setter回调data(){ return{ firstName:'', lastName:'', }},computed:{ fullName:{ get(){ return `${this.firstName} ${this.lastName}`; }, set(newValue){ let names=newValue.split(' '); this.firstName=names[0]; this.lastName=names[1]; } }},$on('hook:生命周期')来简化window监听理论应用先来看一下平时的应用办法,参考vue实战视频解说:进入学习 ...

October 10, 2022 · 2 min · jiezi

关于vue.js:聊聊Vuex原理

背景Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。Vuex 是专门为 Vue.js 设计的状态治理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。如果你曾经灵活运用,然而仍然好奇它底层实现逻辑,无妨一探到底。Vue 组件开发咱们晓得开发 Vue 插件,装置的时候须要执行 Vue.use(Vuex) import Vue from 'vue'import Vuex from '../vuex'Vue.use(Vuex)通过查看 Vue API Vue-use 开发文档,咱们晓得装置 Vue.js 插件。如果插件是一个对象,必须提供 install 办法。如果插件是一个函数,它会被作为 install 办法。install 办法调用时,会将 Vue 作为参数传入。该办法须要在调用 new Vue() 之前被调用。当 install 办法被同一个插件屡次调用,插件将只会被装置一次。为了更好了的去了解源码意思,这里写了一个简略的测试实例。 测试实例代码import Vue from 'vue'import Vuex from '../vuex'Vue.use(Vuex)export default new Vuex.Store({ plugins: [], state: { time: 1, userInfo: { avatar: '', account_name: '', name: '' }, }, getters: { getTime (state) { console.log('1212',state) return state.time } }, mutations: { updateTime(state, payload){ state.time = payload } }, actions: { operateGrou({ commit }) { // commit('updateTime', 100) return Promise.resolve().then(()=>{ return { rows: [1,2,3] } }) } }, modules: { report: { namespaced: true, state: { title: '', }, getters: { getTitle (state) { return state.title } }, mutations: { updateTitle(state, payload){ state.title = payload } }, actions: { operateGrou({ commit }) { commit('updateTitle', 100) return Promise.resolve().then(()=>{ return { rows: [1,2,2,3] } }) } }, modules: { reportChild: { namespaced: true, state: { titleChild: '', }, mutations: { updateTitle(state, payload){ state.title = payload } }, actions: { operateGrou({ commit }) { commit('updateTitle', 100) return Promise.resolve().then(()=>{ return { rows: [1,2,2,3] } }) } }, } } }, part: { namespaced: true, state: { title: '', }, mutations: { updateTitle(state, payload){ state.title = payload }, updateTitle1(state, payload){ state.title = payload } }, actions: { operateGrou({ commit }) { commit('updateTitle', 100) return Promise.resolve().then(()=>{ return { rows: [1,2,2,3] } }) } }, modules: { partChild: { namespaced: true, state: { titleChild: '', }, getters: { getTitleChild (state) { return state.titleChild } }, mutations: { updateTitle(state, payload){ state.titleChild = payload } }, actions: { operateGrou({ commit }) { commit('updateTitle', 1000) return Promise.resolve().then(()=>{ return { rows: [1,2,2,3] } }) } }, modules: { partChildChild: { namespaced: true, state: { titleChild: '', }, getters: { getTitleChild (state) { return state.titleChild } }, mutations: { updateTitle(state, payload){ state.titleChild = payload } }, actions: { operateGrou({ commit }) { commit('updateTitle', 1000) return Promise.resolve().then(()=>{ return { rows: [1,2,2,3] } }) } }, } } } } } }})Graphviz 父子结点关系图用 Graphviz 图来示意一下父子节点的关系,不便了解 ...

October 10, 2022 · 7 min · jiezi

关于vue.js:vue源码分析渲染过程

继上一节内容,咱们将Vue简单的挂载流程通过图解流程,代码剖析的形式简略梳理了一遍,最初也讲到了模板编译的大抵流程。然而在挂载的外围处,咱们并没有剖析模板编译后渲染函数是如何转换为可视化DOM节点的。因而这一章节,咱们将从新回到Vue实例挂载的最初一个环节:渲染DOM节点。在渲染实在DOM的过程中,Vue引进了虚构DOM的概念,这是Vue架构设计中另一个重要的理念。虚构DOM作为JS对象和实在DOM两头的一个缓冲层,对JS频繁操作DOM的引起的性能问题有很好的缓解作用。4.1 Virtual DOM4.1.1 浏览器的渲染流程当浏览器接管到一个Html文件时,JS引擎和浏览器的渲染引擎便开始工作了。从渲染引擎的角度,它首先会将html文件解析成一个DOM树,与此同时,浏览器将辨认并加载CSS款式,并和DOM树一起合并为一个渲染树。有了渲染树后,渲染引擎将计算所有元素的地位信息,最初通过绘制,在屏幕上打印最终的内容。JS引擎和渲染引擎尽管是两个独立的线程,然而JS引擎却能够触发渲染引擎工作,当咱们通过脚本去批改元素地位或外观时,JS引擎会利用DOM相干的API办法去操作DOM对象,此时渲染引擎变开始工作,渲染引擎会触发回流或者重绘。上面是回流重绘的两个概念: 回流: 当咱们对DOM的批改引发了元素尺寸的变动时,浏览器须要从新计算元素的大小和地位,最初将从新计算的后果绘制进去,这个过程称为回流。重绘: 当咱们对DOM的批改只单纯扭转元素的色彩时,浏览器此时并不需要从新计算元素的大小和地位,而只有从新绘制新款式。这个过程称为重绘。很显然回流比重绘更加消耗性能。 通过理解浏览器根本的渲染机制,咱们很容易联想到当一直的通过JS批改DOM时,不经意间会触发到渲染引擎的回流或者重绘,这个性能开销是十分微小的。因而为了升高开销,咱们须要做的是尽可能减少DOM操作。有什么办法能够做到呢? 4.1.2 缓冲层-虚构DOM虚构DOM是为了解决频繁操作DOM引发性能问题的产物。虚构DOM(上面称为Virtual DOM)是将页面的状态形象为JS对象的模式,实质上是JS和实在DOM的中间层,当咱们想用JS脚本大批量进行DOM操作时,会优先作用于Virtual DOM这个JS对象,最初通过比照将要改变的局部告诉并更新到实在的DOM。只管最终还是操作实在的DOM,但Virtual DOM能够将多个改变合并成一个批量的操作,从而缩小 DOM 重排的次数,进而缩短了生成渲染树和绘制所花的工夫。 咱们看一个实在的DOM蕴含了什么: [外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-hhwZKEbz-1665388195868)(https://p1-jj.byteimg.com/tos...)] 浏览器将一个实在DOM设计得很简单,不仅蕴含了本身的属性形容,大小地位等定义,也囊括了DOM领有的浏览器事件等。正因为如此简单的构造,咱们频繁去操作DOM或多或少会带来浏览器的性能问题。而作为数据和实在DOM之间的一层缓冲,Virtual DOM 只是用来映射到实在DOM的渲染,因而不须要蕴含操作 DOM 的办法,它只有在对象中重点关注几个属性即可。 // 实在DOM<div id="real"><span>dom</span></div>// 实在DOM对应的JS对象{ tag: 'div', data: { id: 'real' }, children: [{ tag: 'span', children: 'dom' }]}4.2 VnodeVue在渲染机制的优化上,同样引进了virtual dom的概念,它是用Vnode这个构造函数去形容一个DOM节点。 4.2.1 Vnode构造函数var VNode = function VNode (tag,data,children,text,elm,context,componentOptions,asyncFactory) { this.tag = tag; // 标签 this.data = data; // 数据 this.children = children; // 子节点 this.text = text; ··· ··· };Vnode定义的属性差不多有20几个,显然用Vnode对象要比实在DOM对象形容的内容要简略得多,它只用来单纯形容节点的要害属性,例如标签名,数据,子节点等。并没有保留跟浏览器相干的DOM办法。除此之外,Vnode也会有其余的属性用来扩大Vue的灵活性。 ...

October 10, 2022 · 4 min · jiezi

关于vue.js:vue源码分析挂载流程和模板编译

后面几节咱们从new Vue创立实例开始,介绍了创立实例时执行初始化流程中的重要两步,配置选项的资源合并,以及响应式零碎的核心思想,数据代理。在合并章节,咱们对Vue丰盛的选项合并策略有了根本的认知,在数据代理章节咱们又对代理拦挡的意义和应用场景有了深刻的意识。依照Vue源码的设计思路,初始化过程还会进行很多操作,例如组件之间创立关联,初始化事件核心,初始化数据并建设响应式零碎等,并最终将模板和数据渲染成为dom节点。如果间接按流程的先后顺序剖析每个步骤的实现细节,会有很多概念很难了解。因而在这一章节,咱们先重点剖析一个概念,实例的挂载渲染流程。3.1 Runtime Only VS Runtime + Compiler在注释开始之前,咱们先理解一下vue基于源码构建的两个版本,一个是runtime only(一个只蕴含运行时的版本),另一个是runtime + compiler(一个同时蕴含编译器和运行时的版本)。而两个版本的区别仅在于后者蕴含了一个编译器。 什么是编译器,百度百科这样解释道: 简略讲,编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。一个古代编译器的次要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 指标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)。艰深点讲,编译器是一个提供了将源代码转化为指标代码的工具。从Vue的角度登程,内置的编译器实现了将template模板转换编译为可执行javascript脚本的性能。 3.1.1 Runtime + Compiler一个残缺的Vue版本是蕴含编译器的,咱们能够应用template进行模板编写。编译器会主动将模板字符串编译成渲染函数的代码,源码中就是render函数。如果你须要在客户端编译模板 (比方传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 外部的 HTML 作为模板),就须要一个蕴含编译器的版本。 // 须要编译器的版本new Vue({ template: '<div>{{ hi }}</div>'})3.1.2 Runtime Only只蕴含运行时的代码领有创立Vue实例、渲染并解决Virtual DOM等性能,基本上就是除去编译器外的残缺代码。Runtime Only的实用场景有两种:1.咱们在选项中通过手写render函数去定义渲染过程,这个时候并不需要蕴含编译器的版本便可残缺执行。 // 不须要编译器new Vue({ render (h) { return h('div', this.hi) }})2.借助vue-loader这样的编译工具进行编译,当咱们利用webpack进行Vue的工程化开发时,经常会利用vue-loader对.vue进行编译,只管咱们也是利用template模板标签去书写代码,然而此时的Vue曾经不须要利用编译器去负责模板的编译工作了,这个过程交给了插件去实现。 很显著,编译过程对性能会造成肯定的损耗,并且因为退出了编译的流程代码,Vue代码的总体积也更加宏大(运行时版本相比完整版体积要小大概 30%)。因而在理论开发中,咱们须要借助像webpack的vue-loader这类工具进行编译,将Vue对模板的编译阶段合并到webpack的构建流程中,这样不仅缩小了生产环境代码的体积,也大大提高了运行时的性能,两全其美。 3.2 实例挂载的基本思路有了下面的根底,咱们回头看初始化_init的代码,在代码中咱们察看到initProxy后有一系列的函数调用,这些函数包含了创立组件关联,初始化事件处理,定义渲染函数,构建数据响应式零碎等,最初还有一段代码,在el存在的状况下,实例会调用$mount进行实例挂载。 Vue.prototype._init = function (options) { ··· // 选项合并 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); // 数据代理 initProxy(vm); vm._self = vm; initLifecycle(vm); // 初始化事件处理 initEvents(vm); // 定义渲染函数 initRender(vm); // 构建响应式零碎 initState(vm); // 等等 ··· if (vm.$options.el) { vm.$mount(vm.$options.el); }}以手写template模板为例,理分明什么是挂载。咱们会在选项中传递template为属性的模板字符串,如<div>{{message}}</div>,最终这个模板字符串通过两头过程将其转成实在的DOM节点,并挂载到选项中el代表的根节点上实现视图渲染。这个两头过程就是接下来要剖析的挂载流程。 ...

October 10, 2022 · 4 min · jiezi

关于vue.js:vue源码分析基础的数据代理检测

简略回顾一下这个系列的前两节,前两节花了大量的篇幅介绍了Vue的选项合并,选项合并是Vue实例初始化的开始,Vue为开发者提供了丰盛的选项配置,而每个选项都严格规定了合并的策略。然而这只是初始化中的第一步,这一节咱们将对另一个重点的概念深刻的剖析,他就是数据代理,咱们晓得Vue大量利用了代理的思维,而除了响应式零碎外,还有哪些场景也须要进行数据代理呢?这是咱们这节剖析的重点。2.1 数据代理的含意数据代理的另一个说法是数据劫持,当咱们在拜访或者批改对象的某个属性时,数据劫持能够拦挡这个行为并进行额定的操作或者批改返回的后果。而咱们晓得Vue响应式零碎的外围就是数据代理,代理使得数据在拜访时进行依赖收集,在批改更新时对依赖进行更新,这是响应式零碎的外围思路。而这所有离不开Vue对数据做了拦挡代理。然而响应式并不是本节探讨的重点,这一节咱们将看看数据代理在其余场景下的利用。在剖析之前,咱们须要把握两种实现数据代理的办法: Object.defineProperty 和 Proxy。 2.1.1 Object.defineProperty官网定义:Object.defineProperty()办法会间接在一个对象上定义一个新属性,或者批改一个对象的现有属性, 并返回这个对象。根本用法: Object.defineProperty(obj, prop, descriptor)Object.defineProperty()能够用来准确增加或批改对象的属性,只须要在descriptor对象中将属性个性形容分明,descriptor的属性描述符有两种模式,一种是数据描述符,另一种是存取描述符,咱们别离看看各自的特点。 数据描述符,它领有四个属性配置configurable:数据是否可删除,可配置enumerable:属性是否可枚举value:属性值,默认为undefinedwritable:属性是否可读写存取描述符,它同样领有四个属性选项configurable:数据是否可删除,可配置enumerable:属性是否可枚举get:一个给属性提供 getter 的办法,如果没有 getter 则为 undefined。set:一个给属性提供 setter 的办法,如果没有 setter 则为 undefined。须要留神的是: 数据描述符的value,writable 和 存取描述符中的get, set属性不能同时存在,否则会抛出异样。 有了Object.defineProperty办法,咱们能够不便的利用存取描述符中的getter/setter来进行数据的监听,这也是响应式构建的雏形。getter办法能够让咱们在拜访数据时做额定的操作解决,setter办法使得咱们能够在数据更新时批改返回的后果。看看上面的例子,因为设置了数据代理,当咱们拜访对象o的a属性时,会触发getter执行钩子函数,当批改a属性的值时,会触发setter钩子函数去批改返回的后果。 var o = {}var value;Object.defineProperty(o, 'a', { get() { console.log('获取值') return value }, set(v) { console.log('设置值') value = qqq }})o.a = 'sss' // 设置值console.log(o.a)// 获取值// 'qqq'后面说到Object.defineProperty的get和set办法是对对象进行监测并响应变动,那么数组类型是否也能够监测呢,参照监听属性的思路,咱们用数组的下标作为属性,数组的元素作为拦挡对象,看看Object.defineProperty是否能够对数组的数据进行监控拦挡。 var arr = [1,2,3];arr.forEach((item, index) => { Object.defineProperty(arr, index, { get() { console.log('数组被getter拦挡') return item }, set(value) { console.log('数组被setter拦挡') return item = value } })})arr[1] = 4;console.log(arr)// 后果数组被setter拦挡数组被getter拦挡4显然,已知长度的数组是能够通过索引属性来设置属性的拜访器属性的。然而数组的增加确无奈进行拦挡,这个也很好了解,不论是通过arr.push()还是arr[10] = 10增加的数据,数组所增加的索引值并没有事后退出数据拦挡中,所以天然无奈进行拦挡解决。这个也是应用Object.defineProperty进行数据代理的弊病。为了解决这个问题,Vue在响应式零碎中对数组的办法进行了重写,间接的解决了这个问题,具体细节能够参考后续的响应式系统分析。 ...

October 10, 2022 · 4 min · jiezi

关于vue.js:19-道高频-vue-面试题解答下

既然Vue通过数据劫持能够精准探测数据变动,为什么还须要虚构DOM进行diff检测差别响应式数据变动,Vue的确能够在数据变动时,响应式零碎能够立即得悉。然而如果给每个属性都增加watcher用于更新的话,会产生大量的watcher从而升高性能而且粒度过细也得导致更新不精确的问题,所以vue采纳了组件级的watcher配合diff来检测差别写过自定义指令吗 原理是什么指令实质上是装璜器,是 vue 对 HTML 元素的扩大,给 HTML 元素减少自定义性能。vue 编译 DOM 时,会找到指令对象,执行指令的相干办法。 自定义指令有五个生命周期(也叫钩子函数),别离是 bind、inserted、update、componentUpdated、unbind 1. bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。2. inserted:被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变动。通过比拟更新前后的绑定值,能够疏忽不必要的模板更新。4. componentUpdated:被绑定元素所在模板实现一次更新周期时调用。5. unbind:只调用一次,指令与元素解绑时调用。vue 中应用了哪些设计模式1.工厂模式 - 传入参数即可创立实例 虚构 DOM 依据参数的不同返回根底标签的 Vnode 和组件 Vnode 2.单例模式 - 整个程序有且仅有一个实例 vuex 和 vue-router 的插件注册办法 install 判断如果零碎存在实例就间接返回掉 3.公布-订阅模式 (vue 事件机制) 4.观察者模式 (响应式数据原理) 5.装璜模式: (@装璜器的用法) 6.策略模式 策略模式指对象有某个行为,然而在不同的场景中,该行为有不同的实现计划-比方选项的合并策略 ...其余模式欢送补充 生命周期钩子是如何实现的Vue 的生命周期钩子外围实现是利用公布订阅模式先把用户传入的的生命周期钩子订阅好(外部采纳数组的形式存储)而后在创立组件实例的过程中会一次执行对应的钩子办法(公布) 相干代码如下 export function callHook(vm, hook) { // 顺次执行生命周期对应的办法 const handlers = vm.$options[hook]; if (handlers) { for (let i = 0; i < handlers.length; i++) { handlers[i].call(vm); //生命周期外面的this指向以后实例 } }}// 调用的时候Vue.prototype._init = function (options) { const vm = this; vm.$options = mergeOptions(vm.constructor.options, options); callHook(vm, "beforeCreate"); //初始化数据之前 // 初始化状态 initState(vm); callHook(vm, "created"); //初始化数据之后 if (vm.$options.el) { vm.$mount(vm.$options.el); }};对SSR的了解SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端实现,而后再把html间接返回给客户端 ...

October 10, 2022 · 8 min · jiezi

关于vue.js:19道高频vue面试题解答上

子组件能够间接扭转父组件的数据吗?子组件不能够间接扭转父组件的数据。这样做次要是为了保护父子组件的单向数据流。每次父级组件产生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中收回正告。 Vue提倡单向数据流,即父级 props 的更新会流向子组件,然而反过来则不行。这是为了避免意外的扭转父组件状态,使得利用的数据流变得难以了解,导致数据流凌乱。如果毁坏了单向数据流,当利用简单时,debug 的老本会十分高。 只能通过 $emit 派发一个自定义事件,父组件接管到后,由父组件批改。 用Vue3.0 写过组件吗?如果想实现一个 Modal你会怎么设计?一、组件设计 组件就是把图形、非图形的各种逻辑均形象为一个对立的概念(组件)来实现开发的模式 当初有一个场景,点击新增与编辑都弹框出来进行填写,性能上大同小异,可能只是题目内容或者是显示的主体内容略微不同 这时候就没必要写两个组件,只须要依据传入的参数不同,组件显示不同内容即可 这样,下次开发雷同界面程序时就能够写更少的代码,意义着更高的开发效率,更少的 Bug和更少的程序体积 二、需要剖析 实现一个Modal组件,首先确定须要实现的内容: 遮罩层题目内容主体内容确定和勾销按钮主体内容须要灵便,所以能够是字符串,也能够是一段 html 代码 特点是它们在以后vue实例之外独立存在,通常挂载于body之上 除了通过引入import的模式,咱们还可通过API的模式进行组件的调用 还能够包含配置全局款式、国际化、与typeScript联合 三、实现流程 首先看看大抵流程: 目录构造组件内容实现 API 模式事件处理其余欠缺目录构造 Modal组件相干的目录构造 ├── plugins│ └── modal│ ├── Content.tsx // 保护 Modal 的内容,用于 h 函数和 jsx 语法│ ├── Modal.vue // 根底组件│ ├── config.ts // 全局默认配置│ ├── index.ts // 入口│ ├── locale // 国际化相干│ │ ├── index.ts│ │ └── lang│ │ ├── en-US.ts│ │ ├── zh-CN.ts│ │ └── zh-TW.ts│ └── modal.type.ts // ts类型申明相干因为 Modal 会被 app.use(Modal) 调用作为一个插件,所以都放在plugins目录下 ...

October 10, 2022 · 9 min · jiezi

关于vue.js:Vue2源码一变化侦测

(一)变动侦测初始化Object变动侦测Array变动侦测observe流程图 1、初始化定义Vue构造函数向Vue原型混入操作方法,不便前期扩大在初始化函数中进行 state初始化 -> data初始化// index.jsimport {initMixin} from "init.js"const Vue = function(options){ // 选项初始化 this._init(options);}// 向Vue原型混入操作方法initMixin(Vue);...export default Vue;// init.jsimport {initState} from "state.js"export function initMixin(Vue){ Vue.prototype._init = function(options){ const vm = this; vm.$options = options; // 初始化状态 initState(vm); }}// state.js 初始化状态import {observe} from 'observe/index.js'export function initState(vm){ const opt = vm.$options; if(opt.data){ // 初始化data initData(vm); }}function initData(vm){ let data = vm.$options.data; // 判断data是否为函数 data = typeof data === 'function' ? data.call(vm) : data; // 对对立后的data对象从新挂载在vm实例上 vm._data = data // 数据侦测与劫持 observe(data);}2、Object变动侦测Object.defineProperty()毛病 ...

October 9, 2022 · 2 min · jiezi

关于vue.js:vueelement-动态表格组件

需要1.表头数据不固定2.表头数据多层级3.表格跨行跨列4.表格可编辑5.编辑反对根本表单框 子组件代码dynamic-header.vue<template> <div> <el-form size="medium" class="horizontal" inline> <slot name="search"></slot> </el-form> <div class="title" v-if="title">{{title}}</div> <el-table v-loading="listLoading" :data="list" :stripe="true" class="flexible" :border="true" :span-method="arraySpanMethod" style="overflow: auto" :height="height" ref="table" width="100%" > <template v-for="(item,index) in tableHeader"> <table-column v-if="item.children && item.children.length" :key="Math.random()" :coloumn-header="item"></table-column> <el-table-column v-else class-name="custom" :width="item.width?item.width:'auto'" :fixed="item.fixed" :key="Math.random()" :label="item.tableHeadName" align="center"> <template slot-scope="scope"> <template v-if="item.isEdit"> <el-select filterable v-if='(item.htmlType == "select" || item.htmlType == "radio") && item.dictData' v-model.lazy="scope.row[item.tableFiled]"> <el-option v-for="dict in item.dictData" :key="dict[item.options.value] || dict.value" :label="dict[item.options.label] || dict.label" :value="parseInt(dict[item.options.value] || dict.value)" /> </el-select> <el-date-picker v-else-if='item.htmlType == "datetime" && item.queryType != "BETWEEN"' clearable size="small" v-model.lazy="scope.row[item.tableFiled]" type="date" value-format="yyyy-MM-dd"> </el-date-picker> <el-date-picker v-else-if='item.htmlType == "datetime" && item.queryType == "BETWEEN"' v-model.lazy="scope.row[item.tableFiled]" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="完结日期" ></el-date-picker> <el-input-number v-else-if='item.htmlType == "number"' v-model.lazy="scope.row[item.tableFiled]" clearable size="small" /> <el-input v-else v-model.lazy="scope.row[item.tableFiled]" clearable size="small" /> </template> <template v-else-if="item.template"> {{getTemplate(item.template,scope)}} </template> <template v-else> <dict-tag v-if="item.dictData" :options="item.dictData" :value="scope.row[item.tableFiled]" /> <tempalte v-else-if="item.dictDataArray" > <dict-tag v-for="(it,itIndex) in item.dictDataArray" v-if="scope.row[it.props] == it.value" :key="itIndex" :options="it.dictData" :value="scope.row[item.tableFiled]" /> </tempalte> <template v-else>{{scope.row[item.tableFiled]}}</template> </template> </template> </el-table-column> </template> <el-table-column v-if="pageParamInfo.oper" label="操作" :key="index" fixed="right" width="150"> <template slot-scope="scope"> <slot name="oper" :scope="scope.row"></slot> </template> </el-table-column> </el-table> <slot name="tableFooter" :scope="pageParamInfo.req"></slot> </div></template><script> import TableColumn from './table-column' export default { props: { pageParamInfo: { type: Object, default(){ return {} } }, tableHeader: { type: Array, default(){ return [] } }, RowRules: { type: Array, default(){ return [] } }, list: { type: Object, default(){ return {} } }, title: { type: String, default: "" }, listLoading: { type: Boolean, default: false }, height: { type: String, default: "70vh" }, }, // pageParamInfo 页面列表参数:url req等等; // tableHeader 表头信息 components: { TableColumn, }, data() { return { } }, watch: { tableHeight(val) { this.height = val } }, created() { }, mounted() { }, methods: { getTemplate(template,scope){ return eval(template) }, arraySpanMethod({ row, column, rowIndex, columnIndex }) { let returnMap = { rowspan: 1, colspan: 1 } const data = this.RowRules if (data instanceof Array && data.length > 0) { data.forEach(item => { if (item.col === (columnIndex + 1)) { if (rowIndex % item.row === 0) { returnMap = { rowspan: item.row, colspan: 1 } } else { returnMap = { rowspan: 0, colspan: 0 } } } }) } return returnMap }, // 列表 getList() { this.listLoading = true this.$http.post(this.pageParamInfo.url, this.pageParamInfo.req).then(res => { this.listLoading = false // this.list = res.data.list }) }, // 文件上传中解决 handleExceed(event, file, fileList) { this.upload.isUploading = true }, // 文件上传胜利解决 handleSuccess(response, file, fileList) { this.upload.open = false this.upload.isUploading = false this.$refs.upload.clearFiles() this.$alert(response.message, '导入后果', { dangerouslyUseHTMLString: true, }) this.getList() }, // 重置 reset() { /* this.listLoading = true this.$http.post('busprojecttask/findTaskList', this.req).then(res => { this.listLoading = false this.getList(this.req.MONTH) })*/ }, // 批改 save() { /* this.listLoading = true this.$http.post('busprojecttask/findTaskList', this.req).then(res => { this.listLoading = false this.getList(this.req.MONTH) })*/ } } }</script><style lang="scss" scoped> h1{ color: black !important; text-align: center; padding: 20px; } table{ margin-top: 20px; } .tableClass{ ::v-deep .el-table__fixed{ height: 100% !important; //设置高优先,以笼罩内联款式 } }</style>table-column.vue<template> <el-table-column :label="coloumnHeader.tableHeadName" :prop="coloumnHeader.tableFiled" align="center"> <template v-for="item in coloumnHeader.children"> <tableColumn v-if="item.children && item.children.length" :key="Math.random()" :coloumn-header="item"></tableColumn> <el-table-column :fixed="item.fixed" :width="item.width?item.width:'auto'" v-else :key="Math.random()" :label="item.tableHeadName" :prop="item.tableFiled" align="center"> <template slot-scope="scope"> <template v-if="item.isEdit"> <el-select v-if='(item.htmlType == "select" || item.htmlType == "radio") && item.dictData' v-model.lazy="scope.row[item.tableFiled]"> <el-option v-for="dict in item.dictData" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)" /> </el-select> <el-date-picker v-else-if='item.htmlType == "datetime" && item.queryType != "BETWEEN"' clearable size="small" v-model.lazy="scope.row[item.tableFiled]" type="date" value-format="yyyy-MM-dd"> </el-date-picker> <el-date-picker v-else-if='item.htmlType == "datetime" && item.queryType == "BETWEEN"' v-model.lazy="scope.row[item.tableFiled]" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="完结日期" ></el-date-picker> <el-input-number v-else-if='item.htmlType == "number"' v-model.lazy="scope.row[item.tableFiled]" clearable size="small" /> <el-input v-else v-model.lazy="scope.row[item.tableFiled]" clearable size="small" /> </template> <template v-else-if="item.template"> {{getTemplate(item.template,scope)}} </template> <template v-else> <dict-tag v-if="item.dictData" :options="item.dictData" :value="scope.row[item.tableFiled]" /> <tempalte v-else-if="item.dictDataArray" > <dict-tag v-for="(it,itIndex) in item.dictDataArray" v-if="scope.row[it.props] == it.value" :key="itIndex" :options="it.dictData" :value="scope.row[item.tableFiled]" /> </tempalte> <template v-else>{{scope.row[item.tableFiled]}}</template> </template> </template> </el-table-column> </template> </el-table-column></template><script>export default { name: 'TableColumn', props: { coloumnHeader: { type: Object, required: true } }}</script><style scoped></style>父组件list为表格数据,tableHeader为表头数据 ...

October 9, 2022 · 3 min · jiezi

关于vue.js:Vue项目处理错误上报如此简单

解决异样的意义随着网页我的项目越来越简单,许多异样报错很难在开发和测试阶段被发现,只管你可能避开了语法等惯例谬误,但不可避免的是代码在运行时的谬误你仍旧无奈精确意料,假如当初有如下一段 Vue 代码,它在生命周期的 created 阶段异步申请并接管了谬误的数据,可能就会导致页面渲染呈现谬误: <template> {{ test.obj.xxx }}</template>......created() { this.getSomeData()},methods: { getSomeData() { this.fetch().then((res) => { this.test = res // 假如这是申请的谬误数据 }) },}而如果测试人员及时发现了这一谬误的话,当他关上控制台时往往就会立刻下结论了:噢,是前端的锅 事实上真正的我的项目中可能会遇到更多"微妙"的问题,而且如果谬误仅产生在某些用户端,那将无从觉察,于是咱们会想到应该在程序中解决捕捉运行时谬误,将谬误上报至服务器,而后剖析和改良代码来修复曾经产生的谬误。 所以该如何应答并解决可能产生的某些谬误,成为了前端开发的一门必修课,你当然能够在每个代码片段中反复编写 try...catch...、为每个 Promise 都解决 catch,但这未免显得有些狼狈,于是我思考能不能用更优雅的形式,对立解决所有异样,将谬误在全局进行捕捉而后上报剖析。其实在 Vue 中实现这样全局的异样解决并不难,上面看看我是如何做的吧。 如何全局捕捉谬误异样查问 Vue 文档咱们能够发现全局配置中就有这么一个捕捉谬误的解决钩子 errorHandler,用法很简略: Vue.config.errorHandler = function (err, vm, info) { // `info` 是 Vue 特定的错误信息,比方谬误所在的生命周期钩子 // 只在 2.2.0+ 可用 }只须要用这个钩子就能够解决大部分 Vue 利用中的谬误(如组件生命周期中的谬误、自定义事件处理函数外部谬误、v-on DOM 监听器外部抛出的谬误),并且回调中自带的 info 参数也标记了这个谬误大略是属于哪类,同时它还能解决返回 Promise 链的谬误,能够说是十分弱小了,然而它也并非能解决所有的异样,否则文章写到这就该完结了 ~ 接下来咱们测试一下。 首先在全局谬误捕捉中输入一下 log,先运行一下结尾的申请数据谬误例子: Vue.config.errorHandler = function (err, vm, info) { console.log('vue异样谬误捕捉: ', '谬误产生在 ' + info)} ...

October 6, 2022 · 3 min · jiezi

关于vue.js:每日一题之请描述Vue组件渲染流程

组件化是 Vue, React 等这些框架的一个核心思想,通过把页面拆成一个个高内聚、低耦合的组件,能够极大水平进步咱们的代码复用度,同时也使得我的项目更加易于保护。所以,本文就来剖析下组件的渲染流程。咱们通过上面这个例子来进行剖析: <div id="demo"> <comp></comp></div><script> Vue.component('comp', { template: '<div>I am comp</div>', }) const app = new Vue({ el: '#demo', })</script>这里咱们分为两步来剖析:组件申明、组件创立及渲染 组件申明首先,咱们看下 Vue.component 是什么货色,它的申明在 core/global-api/assets.js: export function initAssetRegisters(Vue: GlobalAPI) { // ASSET_TYPES是数组:['component','directive','filter'] ASSET_TYPES.forEach((type) => { Vue[type] = function ( id: string, definition: Function | Object ): Function | Object | void { if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && type === 'component') { validateComponentName(id) } // 组件申明相干代码 if (type === 'component' && isPlainObject(definition)) { definition.name = definition.name || id // _base是Vue // Vue.extend({})返回组件构造函数 definition = this.options._base.extend(definition) } if (type === 'directive' && typeof definition === 'function') { definition = {bind: definition, update: definition} } // 注册到components选项中去 // 在Vue原始选项上增加组件配置,未来其余组件继承,它们都有这些组件注册 this.options[type + 's'][id] = definition return definition } } })}这里 this.options._base.extend(definition) 调用的其实就是 Vue.extend(definition): ...

October 6, 2022 · 6 min · jiezi

关于vue.js:每日一题之Vue的异步更新实现原理是怎样的

最近面试总是会被问到这么一个问题:在应用vue的时候,将for循环中申明的变量i从1减少到100,而后将i展现到页面上,页面上的i是从1跳到100,还是会怎么?答案当然是只会显示100,并不会有跳转的过程。 怎么能够让页面上有从1到100显示的过程呢,就是用setTimeout或者Promise.then等办法去模仿。 讲道理,如果不在vue里,独自运行这段程序的话,输入肯定是从1到100,然而为什么在vue中就不一样了呢? for(let i=1; i<=100; i++){ console.log(i);}这就波及到Vue底层的异步更新原理,也要说一说nextTick的实现。不过在说nextTick之前,有必要先介绍一下JS的事件运行机制。 JS运行机制家喻户晓,JS是基于事件循环的单线程的语言。执行的步骤大抵是: 当代码执行时,所有同步的工作都在主线程上执行,造成一个执行栈;在主线程之外还有一个工作队列(task queue),只有异步工作有了运行后果就在工作队列中搁置一个事件;一旦执行栈中所有同步工作执行结束(主线程代码执行结束),此时主线程不会闲暇而是去读取工作队列。此时,异步的工作就完结期待的状态被执行。主线程一直反复以上的步骤。 咱们把主线程执行一次的过程叫一个tick,所以nextTick就是下一个tick的意思,也就是说用nextTick的场景就是咱们想在下一个tick做一些事的时候。所有的异步工作后果都是通过工作队列来调度的。而工作分为两类:宏工作(macro task)和微工作(micro task)。它们之间的执行规定就是每个宏工作完结后都要将所有微工作清空。常见的宏工作有setTimeout/MessageChannel/postMessage/setImmediate,微工作有MutationObsever/Promise.then。 nextTick原理派发更新大家都晓得vue的响应式的靠依赖收集和派发更新来实现的。在批改数据之后的派发更新过程,会触发setter的逻辑,执行dep.notify(): // src/core/observer/watcher.jsclass Dep { notify() { //subs是Watcher的实例数组 const subs = this.subs.slice() for(let i=0, l=subs.length; i<l; i++){ subs[i].update() } }}遍历subs里每一个Watcher实例,而后调用实例的update办法,上面咱们来看看update是怎么去更新的: class Watcher { update() { ... //各种状况判断之后 else{ queueWatcher(this) } }}update执行后又走到了queueWatcher,那就持续去看看queueWatcher干啥了(心愿不要持续套娃了: //queueWatcher 定义在 src/core/observer/scheduler.jsconst queue: Array<Watcher> = []let has: { [key: number]: ?true } = {}let waiting = falselet flushing = falselet index = 0export function queueWatcher(watcher: Watcher) { const id = watcher.id //依据id是否反复做优化 if(has[id] == null){ has[id] = true if(!flushing){ queue.push(watcher) }else{ let i=queue.length - 1 while(i > index && queue[i].id > watcher.id){ i-- } queue.splice(i + 1, 0, watcher) } if(!waiting){ waiting = true //flushSchedulerQueue函数: Flush both queues and run the watchers nextTick(flushSchedulerQueue) } }}这里queue在pushwatcher时是依据id和flushing做了一些优化的,并不会每次数据扭转都触发watcher的回调,而是把这些watcher先增加到⼀个队列⾥,而后在nextTick后执⾏flushSchedulerQueue。 ...

October 6, 2022 · 2 min · jiezi

关于vue.js:每日一题之Vue数据劫持原理是什么

什么是数据劫持?定义: 数据劫持,指的是在拜访或者批改对象的某个属性时,通过一段代码拦挡这个行为,进行额定的操作或者批改返回后果。 简略地说,就是当咱们 触发函数的时候 动一些手脚做点咱们本人想做的事件,也就是所谓的 "劫持"操作 数据劫持的两种计划:Object.definePropertyProxy1).Object.defineProperty语法:Object.defineProperty(obj,prop,descriptor) 参数: obj:指标对象prop:须要定义的属性或办法的名称descriptor:指标属性所领有的个性可供定义的个性列表: value:属性的值writable:如果为false,属性的值就不能被重写。get: 一旦指标属性被拜访就会调回此办法,并将此办法的运算后果返回用户。set:一旦指标属性被赋值,就会调回此办法。configurable:如果为false,则任何尝试删除指标属性或批改属性性以下个性(writable, configurable, enumerable)的行为将被有效化。enumerable:是否能在for...in循环中遍历进去或在Object.keys中列举进去。例子在Vue中其实就是通过Object.defineProperty来劫持对象属性的setter和getter操作,并“种下”一个监听器,当数据发生变化的时候发出通知,如下: var data = {name:'test'}Object.keys(data).forEach(function(key){ Object.defineProperty(data,key,{ enumerable:true, configurable:true, get:function(){ console.log('get'); }, set:function(){ console.log('监听到数据产生了变动'); } })});data.name //控制台会打印出 “get”data.name = 'hxx' //控制台会打印出 "监听到数据产生了变动"下面的这个例子能够看出,咱们齐全能够管制对象属性的设置和读取。在Vue中,在很多中央都十分奇妙的使用了Object.defineProperty这个办法,具体用在哪里并且它又解决了哪些问题,上面就简略的说一下: 监听对象属性的变动它通过observe每个对象的属性,增加到订阅器dep中,当数据发生变化的时候收回一个notice。 相干源代码如下:(作者采纳的是ES6+flow写的,代码在src/core/observer/index.js模块外面) export function defineReactive ( obj: Object, key: string, val: any, customSetter?: Function) { const dep = new Dep()//创立订阅对象 const property = Object.getOwnPropertyDe述 //属性的形容个性外面如果configurable为false则属性的任何批改将有效 if (property && property.configurable === false) { return }scriptor(obj, key)//获取obj对象的key属性的描 // cater for pre-defined getter/setters const getter = property && property.get const setter = property && property.set let childOb = observe(val)//创立一个观察者对象 Object.defineProperty(obj, key, { enumerable: true,//可枚举 configurable: true,//可批改 get: function reactiveGetter () { const value = getter ? getter.call(obj) : val//先调用默认的get办法取值 //这里就劫持了get办法,也是作者一个奇妙设计,在创立watcher实例的时候,通过调用对象的get办法往订阅器dep上增加这个创立的watcher实例 if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } } return value//返回属性值 }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val//先取旧值 if (newVal === value) { return } //这个是用来判断生产环境的,能够忽视 if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = observe(newVal)//持续监听新的属性值 dep.notify()//这个是真正劫持的目标,要对订阅者发告诉了 } })}以上是Vue监听对象属性的变动,那么问题来了,咱们常常在传递数据的时候往往不是一个对象,很有可能是一个数组,那是不是就没有方法了呢,答案显然是否则的。那么上面就看看作者是如何监听数组的变动: ...

October 6, 2022 · 4 min · jiezi

关于vue.js:Vue的computed和watch的区别是什么

一、computed介绍computed 用来监控本人定义的变量,该变量在 data 内没有申明,间接在 computed 外面定义,页面上可间接应用。 //根底应用{{msg}}<input v-model="name" /> //计算属性 computed:{ msg:function(){ return this.name }}在输入框中,扭转 name 值得时候,msg 也会跟着扭转。这是因为 computed 监听本人的属性 msg,发现 name 一旦变动,msg 立马会更新。 留神:msg 不可在 data 中定义,否则会报错。 1.1、get 和 set 用法 <input v-model="full" ><br><input v-model="first" > <br><input v-model="second" > data(){ return{ first:'美女', second:'姐姐' }},computed:{ full:{ get(){ //回调函数 当须要读取以后属性值是执行,依据相干数据计算并返回以后属性的值 return this.first + ' ' + this.second }, set(val){ //监督以后属性值的变动,当属性值发生变化时执行,更新相干的属性数据 let names = val.split(' ') this.first = names[0] this.second = names[1] } }}get 办法:first 和 second 扭转时,会调用 get 办法,更新 full 的值。 ...

October 6, 2022 · 2 min · jiezi

关于vue.js:vue3中使用swiper8

最新swiper应用记录 swiper中文APIswiper英文版API 倡议看此API "vue": "^3.2.37""swiper": "8.3.2" 装置 npm i swiper在组件中导入swiperimport { Swiper, SwiperSlide } from "swiper/vue";// 这是分页器和对应办法,swiper如同在6的时候就曾经拆散了分页器和一些其余工具import SwiperCore, { Autoplay, Navigation, Pagination, A11y } from "swiper";SwiperCore.use([Navigation, Pagination, Scrollbar, A11y, Autoplay]);// 引入swiper款式,对应css 如果应用less或者css只须要把scss改为对应的即可import "swiper/css";import "swiper/css/navigation";import "swiper/css/pagination";const modules = [Autoplay, Pagination, Navigation, A11y];特地阐明SwiperCore须要通过它来应用裸露的属性SwiperCore.use([Navigation, Pagination, Scrollbar, A11y, Autoplay])在模板中应用<template> <swiper :slidesPerView="1" :spaceBetween="30" :loop="true" :centeredSlides="true" :pagination="{ clickable: true, }" :autoplay="{ delay: 1000, disableOnInteraction: false, }" :navigation="true" :modules="modules" class="mySwiper" ref="mySwiper" > <swiper-slide v-for="(item, index) in imgList" :key="index"> <div style="width: 100%" class="flex"> <img style="width: 100%; height: 100%" :src="item" /> </div> </swiper-slide> </swiper></template><script setup> // 这是分页器和对应办法,swiper如同在6的时候就曾经拆散了分页器和一些其余工具 import SwiperCore, { Autoplay, Navigation, Pagination, A11y } from 'swiper'; const modules = [Autoplay, Pagination, Navigation, A11y]; import 'swiper/css' import 'swiper/css/navigation' // import 'swiper/css/pagination' // import 'swiper/css/bundle' const imgList = ref(['https://www.baidu.com/img/bdlogo.png'])</script>局部参数阐明::slidesPerView="1" //每页显示几个:spaceBetween="30" //每个间距是多少:loop="true" //循环滚动:centeredSlides="true" //值为【false】时左对齐,默认就是左对齐,值为【true】时居中对齐:pagination="{ clickable: true, }"//点击分页圆点是否切换:autoplay="{ delay: 1000, disableOnInteraction: false,}"//设置多少毫秒会执行一次动画(能够了解为:翻页 / 切换):navigation="true" //左右切换箭头自定义箭头(留神点)通过应用以下导入并且应用 ...

October 5, 2022 · 1 min · jiezi

关于vue.js:说说你对Vue的keepalive的理解

什么是 keep-alive在平时开发中,有局部组件没有必要屡次初始化,这时,咱们须要将组件进行长久化,使组件的状态维持不变,在下一次展现时,也不会进行从新初始化组件。 也就是说,keepalive 是 Vue 内置的一个组件,能够使被蕴含的组件保留状态,或防止从新渲染 。也就是所谓的组件缓存 <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,避免反复渲染DOM。 <keep-alive> 包裹动静组件时,会缓存不流动的组件实例,而不是销毁它们。和 <transition> 类似,<keep-alive> 是一个形象组件:它本身不会渲染一个 DOM 元素,也不会呈现在父组件链中。prop: include: 字符串或正则表达式。只有匹配的组件会被缓存。exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。keep-alive的申明周期执行页面第一次进入,钩子的触发程序 created-> mounted-> activated, 退出时触发 deactivated 当再次进入(后退或者后退)时,只触发 activated事件挂载的办法等,只执行一次的放在 mounted 中;组件每次进去执行的办法放在 activated 中;根本用法<!--被keepalive蕴含的组件会被缓存--><keep-alive> <component><component /></keep-alive>被keepalive蕴含的组件不会被再次初始化,也就意味着不会重走生命周期函数 然而有时候是心愿咱们缓存的组件能够可能再次进行渲染,这时 Vue 为咱们解决了这个问题被蕴含在 keep-alive 中创立的组件,会多出两个生命周期的钩子: activated 与 deactivated: activated 当 keepalive 蕴含的组件再次渲染的时候触发deactivated 当 keepalive 蕴含的组件销毁的时候触发keepalive是一个形象的组件,缓存的组件不会被 mounted,为此提供activated和deactivated钩子函数 更多面试题: 前端vue面试题具体解答 参数了解keepalive 能够接管3个属性做为参数进行匹配对应的组件进行缓存: include 蕴含的组件(能够为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)exclude 排除的组件(认为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)max 缓存组件的最大值(类型为字符或者数字,能够管制缓存组件的个数)注:当应用正则表达式或者数组时,肯定要应用 v-bind <!-- 将(只)缓存组件name为a或者b的组件, 联合动静组件应用 --><keep-alive include="a,b"> <component></component></keep-alive><!-- 组件name为c的组件不缓存(能够保留它的状态或防止从新渲染) --><keep-alive exclude="c"> <component></component></keep-alive><!-- 应用正则表达式,需应用v-bind --><keep-alive :include="/a|b/"> <component :is="view"></component></keep-alive><!-- 动静判断 --><keep-alive :include="includedComponents"> <router-view></router-view></keep-alive><!-- 如果同时应用include,exclude,那么exclude优先于include, 上面的例子只缓存a组件 --><keep-alive include="a,b" exclude="b"> <component></component></keep-alive><!-- 如果缓存的组件超过了max设定的值5,那么将删除第一个缓存的组件 --><keep-alive exclude="c" max="5"> <component></component></keep-alive>遇见 vue-router 联合router应用,缓存局部页面所有门路下的视图组件都会被缓存<keep-alive> <router-view> <!-- 所有门路匹配到的视图组件都会被缓存! --> </router-view></keep-alive>如果只想要router-view外面的某个组件被缓存,怎么办?应用 include/exclude应用 meta 属性1、用 include (exclude例子相似) ...

October 5, 2022 · 2 min · jiezi

关于vue.js:Vue是怎样监听数组的变化的

上周五跟着一个师姐面试一个三年工作教训的前端开发,我在一边审慎的观摩。想着已经我也被他人面试过,现在面试他人,感觉其实情绪是一样的。前言 工作三年的Vue使用者应该懂什么?为何工作几年的根底越来越弱?工作如何挤出工夫学习?一道面试题其实咱们并不是要你把答案都记下来,而是把其中的思维学习到。就像你接触一个新的畛域react,你也一样能够把根本思维提炼进去。 面试题: Vue是如何对数据进行监听的? 这其实是陈词滥调的问题,凡是你有一点基础知识,你也能答出一二。师姐跟我说,其实问题不只是问题自身,而是跟这个常识顺带进去的体系。 01 对象数据是怎么被监听的 在vue2.x版本中,数据监听是用过Object.defineProperty这个API来实现的,咱们能够来看一个例子 var text = 'vue';const data = {};Object.defineProperty(data, 'text', { get() { return text; }, set(newVal) { text = newVal; }});data.text // 'vue'data.text = 'react' // 'react'当咱们拜访或设置对象的属性的时候,都会触发绝对应的函数,而后在这个函数里返回或设置属性的值。咱们当然能够在触发函数的时候做咱们本人想做的事件,这也就是“劫持”操作。 在Vue中其实就是通过Object.defineProperty来劫持对象属性的setter和getter操作,并创立一个监听器,当数据发生变化的时候发出通知。 var data = { name:'hello', age:18}Object.keys(data).forEach(function(key){ Object.defineProperty(data,key,{ enumerable:true, // 是否能在for...in循环中遍历进去或在Object.keys中列举进去。 configurable:true, // false,不可批改、删除指标属性或批改属性性以下个性 get:function(){ console.log('获取数据'); }, set:function(){ console.log('监听到数据产生了变动'); } })});data.name //控制台会打印出 “获取数据”data.name = 'world' //控制台会打印出 "监听到数据产生了变动"参考 前端vue面试题具体解答 02 数组数据是怎么被监听的 咱们晓得,下面是对对象的数据进行监听的,咱们不能对数组进行数据的“劫持”。那么Vue是怎么做的呢? import { def } from '../util/index'const arrayProto = Array.prototypeexport const arrayMethods = Object.create(arrayProto)const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']methodsToPatch.forEach(function (method) { // 缓存原来的办法 const original = arrayProto[method] def(arrayMethods, method, function mutator (...args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result })})看来Vue能对数组进行监听的起因是,把数组的办法重写了。总结起来就是这几步: ...

October 5, 2022 · 2 min · jiezi

关于vue.js:说说Vue响应式系统中的Watcher和Dep的关系面试进阶

引言在这里我先提出两个问题(文章开端会进行解答): 在Vue的数据响应零碎中,Dep和Watcher各自分担什么工作?Vue的数据响应零碎的外围是Object.defineproperty肯定是最好的吗?有什么弊病和破绽吗?一、什么是响应零碎中的Watcher,它的作用是什么?响应零碎中的Watcher即这个零碎的观察者,它是响应零碎中观察者模式的载体,当响应零碎中的数据产生扭转的时候,它可能晓得并且执行相应的函数以达到某种业务逻辑的目标。打个比方,如果你是一个商家,要寄一批货别离给不同的客户,那么watcher就是一个个快递员,收回的动作就是数据产生扭转。你只须要负责寄出去这个动作就行了,如何找到、送到客户则是watcher的事件。 每个watcher和数据之间的关系要么是1对1,要么是多对多关系(这与watcher的类型无关),要不是没有分割。watcher和业务逻辑只有1对1关系。 二、Watcher的类型在Vue源码中是没有体现出Watcher的类型的,我在这里给Watcher增加类型是为了更好地了解Watcher这个对象。Watcher在一般的业务逻辑上能够分为以下三类: 一般的Watcher:与数据1对1关系。lazy型Watcher:与数据1对1关系,然而它是一个惰性求值的观察者,怎么体现呢?对它进行赋值是不会扭转它的值,只有当获取它的值的时候,才会更新最新版的数据(在Vue中体现为computed办法,个别求值是通过办法来求值的)。render型Watcher:与数据是1对多(不思考传参进子组件)的关系,在一个组件中,渲染函数观察者肯定是最初生成的,所以执行观察者队列的时候,渲染函数观察者在一个组件中是最初执行的。在这里多嘴一下lazy型的观察者是怎么回事吧。lazy型观察者在Vue中体现为computed属性,个别这个属性是一个函数,以下是一个例子: computed: { // getCount最初解决成一个属性,而后这个办法被存储在Watcher的某个属性中 getCount() { return this.a + this.b; }}lazy观察者外面有一个dirty属性,也就是一个开关作用,只有它为true的时候应用getCount的getter办法的时候,才会进行调用这个函数。 如果lazy观察者所援用的数据(a或者b属性)产生扭转后,会将这个放到观察者执行队列中,而后执行这个观察者的时候把dirty赋值为true(代表下次访问getter办法的时候会执行一遍lazy的求值办法(求值后会将dirty赋值为false))。等到下一次须要获取这个数据的时候才进行求值,所以它叫做惰性求值。这种形式可能节俭不必要执行函数的开销。 参考 前端vue面试题具体解答 三、Watcher和Dep的关系看过Vue源码的defineReactive这个办法,就会发现一个被察看的对象外面每个属性会有一个Dep依赖筐来寄存所有察看它的Watcher。而defineReactive办法只有初始化每个属性的dep却并没有创立观察者(要分清初始化和创立观察者是离开这个事实)。那么这个Dep如何增加观察者呢?Vue应用了全局变量,这个变量叫做Dep.target,它是一个Watcher类型的变量,来将Watcher和Dep进行相互绑定。数据的绑定用图来示意的话如下: 咱们能够明确以下区别: $watch办法创立的观察者的时候,如果不设定immediate属性,那么是不会进行调用的,而computed和render是会进行调用办法的。数据的Dep的subs数组寄存这个数据所绑定的观察者对象,观察者对象的deps数组中寄存着与这个观察者无关的数据Dep。所以数据的Dep与Watcher其实是多对多关系$watch和computed观察者是在created生命钩子函数前就创立结束并且绑定的,而render观察者是在mounted之前创立并绑定的,所以同一个组件中,render观察者的id会大于其余观察者(id是在前面执行队列外面升序排序的时候的根据)。 换句话说,在同一个组件的观察者中,当数据产生扭转的时候,渲染函数观察者肯定是最初执行的。 这个很好了解,其余观察者中难免会对数据进行批改,如果渲染函数观察者先执行了,而后其余观察者对数据进行扭转的话,那么没方法将数据精确出现在页面上,导致数据不一致性。四、讲一下观察者执行队列机制Vue是如何实现性能优化的呢?最显著的两个点: 观察者设定执行队列,批量执行。diff算法缩小渲染开销。第二个不在这外面解说,咱们看一下第一个是怎么回事? 这个队列的长度是怎么定量的呢? 最大长度是100,源码摆在那里。 以一个事件循环时间段为收集工夫。(什么是事件循环?能够看一下本博客零碎的其余优良文章)它的流程是如下的: 未执行时候:如果有更改过数据,那么就将对应的观察者间接推动队列中(执行的时候会进行依据id升序排序后执行)在执行中的时候,如果有新的观察者进来了(观察者中更改数据,而后这个数据又绑定观察者),依照id升序来进行插入(这相当于在有序数组外面进行插入,能够看做插入排序的其中一步,所以某种意义上来说它就是排序)。五、解答后面的问题Dep是负责存放数据所绑定所有的观察者的对象的容器,只有数据产生扭转,就会通过这个Dep来告诉所有观察者进行批改数据。(每个数据都有举世无双的Dep)。而Watcher更偏差于一个动作,也就是规定的业务逻辑或者渲染函数,是一个执行者。在ES5是很轻便的,很好的。然而在ES6呈现后,它就肯定不是最好的,因为ES6有一个Proxy代理来对立进行解决。(ES6应用代理实现Vue数据响应零碎(TypeScript)) 弊病:如果一个数据有1000个属性,那么就要给这1000个属性应用Object.defineProperty,这样在初始化页面的时候会造成卡顿。如果用代理的话,那么只须要执行一次就能够了。破绽:如果咱们拿到了vm实例,那么用户是能够在运行的时候通过批改对象属性的描述符(descriptor)来进行批改它,会造成零碎的不确定性。这是因为响应零碎的模式导致必须将数据的描述符的configuration设为true,所以在运行的时候可能对它进行批改。

October 5, 2022 · 1 min · jiezi

关于vue.js:vue源码中的渲染过程是怎样的

4.1 Virtual DOM4.1.1 浏览器的渲染流程当浏览器接管到一个Html文件时,JS引擎和浏览器的渲染引擎便开始工作了。从渲染引擎的角度,它首先会将html文件解析成一个DOM树,与此同时,浏览器将辨认并加载CSS款式,并和DOM树一起合并为一个渲染树。有了渲染树后,渲染引擎将计算所有元素的地位信息,最初通过绘制,在屏幕上打印最终的内容。JS引擎和渲染引擎尽管是两个独立的线程,然而JS引擎却能够触发渲染引擎工作,当咱们通过脚本去批改元素地位或外观时,JS引擎会利用DOM相干的API办法去操作DOM对象,此时渲染引擎变开始工作,渲染引擎会触发回流或者重绘。上面是回流重绘的两个概念: 回流: 当咱们对DOM的批改引发了元素尺寸的变动时,浏览器须要从新计算元素的大小和地位,最初将从新计算的后果绘制进去,这个过程称为回流。重绘: 当咱们对DOM的批改只单纯扭转元素的色彩时,浏览器此时并不需要从新计算元素的大小和地位,而只有从新绘制新款式。这个过程称为重绘。很显然回流比重绘更加消耗性能。 通过理解浏览器根本的渲染机制,咱们很容易联想到当一直的通过JS批改DOM时,不经意间会触发到渲染引擎的回流或者重绘,这个性能开销是十分微小的。因而为了升高开销,咱们须要做的是尽可能减少DOM操作。有什么办法能够做到呢? 4.1.2 缓冲层-虚构DOM虚构DOM是为了解决频繁操作DOM引发性能问题的产物。虚构DOM(上面称为Virtual DOM)是将页面的状态形象为JS对象的模式,实质上是JS和实在DOM的中间层,当咱们想用JS脚本大批量进行DOM操作时,会优先作用于Virtual DOM这个JS对象,最初通过比照将要改变的局部告诉并更新到实在的DOM。只管最终还是操作实在的DOM,但Virtual DOM能够将多个改变合并成一个批量的操作,从而缩小 DOM 重排的次数,进而缩短了生成渲染树和绘制所花的工夫。 咱们看一个实在的DOM蕴含了什么: 浏览器将一个实在DOM设计得很简单,不仅蕴含了本身的属性形容,大小地位等定义,也囊括了DOM领有的浏览器事件等。正因为如此简单的构造,咱们频繁去操作DOM或多或少会带来浏览器的性能问题。而作为数据和实在DOM之间的一层缓冲,Virtual DOM 只是用来映射到实在DOM的渲染,因而不须要蕴含操作 DOM 的办法,它只有在对象中重点关注几个属性即可。 // 实在DOM<div id="real"><span>dom</span></div>// 实在DOM对应的JS对象{ tag: 'div', data: { id: 'real' }, children: [{ tag: 'span', children: 'dom' }]}参考vue源码视频解说:进入学习4.2 VnodeVue在渲染机制的优化上,同样引进了virtual dom的概念,它是用Vnode这个构造函数去形容一个DOM节点。 4.2.1 Vnode构造函数var VNode = function VNode (tag,data,children,text,elm,context,componentOptions,asyncFactory) { this.tag = tag; // 标签 this.data = data; // 数据 this.children = children; // 子节点 this.text = text; ··· ··· };Vnode定义的属性差不多有20几个,显然用Vnode对象要比实在DOM对象形容的内容要简略得多,它只用来单纯形容节点的要害属性,例如标签名,数据,子节点等。并没有保留跟浏览器相干的DOM办法。除此之外,Vnode也会有其余的属性用来扩大Vue的灵活性。 ...

October 4, 2022 · 4 min · jiezi

关于vue.js:从vue源码中学习观察者模式

摘要:源码解读设计模式系列文章将陆陆续续进行更新中 ~摘要:源码解读设计模式系列文章将陆陆续续进行更新中 ~ 观察者模式首先话题下来,咱们得反诘一下本人,什么是观察者模式? 概念观察者模式(Observer):通常又被称作为公布-订阅者模式。它定义了一种一对多的依赖关系,即当一个对象的状态产生扭转的时候,所有依赖于它的对象都会失去告诉并自动更新,解决了主体对象与观察者之间性能的耦合。 讲个故事下面对于观察者模式的概念可能会比拟官网化,所以咱们讲个故事来了解它。 A:是密探,代号 001(发布者)B:是通信人员,负责与 A 进行机密交接(订阅者)A 日常工作就是在明面采集一些情报B 则负责暗中察看着 A一旦 A 传递出一些无关音讯(更多时候须要对音讯进行封装传递,前面依据源码具体分析)B 会立马订阅到该音讯,而后做一些绝对应的变更,比如说告诉做一些事件应答的一些动作。适用性以下任一场景都能够应用观察者模式 当一个形象模型有两个方面,其中一个方面依赖于另一方面。讲这两者封装在独立的对象中能够让它们能够各自独立的扭转和复用当一个对象的扭转的时候,须要同时扭转其它对象,然而却不晓得具体多少对象有待扭转当一个对象必须告诉其它对象,然而却不晓得具体对象到底是谁。换句话说,你不心愿这些对象是严密耦合的。vue 对于观察者模式的应用vue 应用到观察者模式的中央有很多,这里咱们次要谈谈对于数据初始化这一块的。 var vm = new Vue({ data () { return { a: 'hello vue' } }})1、实现数据劫持上图咱们能够看到,vue 是利用的是 Object.defineProperty() 对数据进行劫持。 并在数据传递变更的时候封装了一层中转站,即咱们看到的 Dep 和 Watcher 两个类。 这一大节,咱们只看如何通过观察者模式对数据进行劫持。 1.1、递归遍历咱们都晓得,vue 对于 data 外面的数据都做了劫持的,那只能对对象进行遍历从而实现每个属性的劫持,源码具体如下 walk (obj: Object) { const keys = Object.keys(obj) // 遍历将其变成 vue 的拜访器属性 for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) }}参考vue源码视频解说:进入学习1.2、公布/订阅从下面对象的遍历咱们看到了 defineReactive ,那么劫持最要害的点也在于这个函数,该函数外面封装了 getter 和 setter 函数,应用观察者模式,相互监听 ...

October 4, 2022 · 7 min · jiezi

关于vue.js:VuenextTick的原理是什么vue面试进阶

原理性的货色就会文字较多,请耐下心来,细细品味 Vue中DOM更新机制当你威风凛凛地应用Vue大展宏图的时候,忽然发现,咦,我明明对这个数据进行更改了,然而当我获取它的时候怎么是上一次的值(自己比拟懒,就不具体举例了) 此时,Vue就会说:“小样,这你就不懂了吧,我的DOM是异步更新的呀!!!” 简略的说,Vue的响应式并不是只数据发生变化之后,DOM就立即发生变化,而是依照肯定的策略进行DOM的更新。这样的益处是能够防止一些对DOM不必要的操作,进步渲染性能。 在Vue官网文档中是这样阐明的: 可能你还没有留神到,Vue异步执行DOM更新。只有察看到数据变动,Vue将开启一个队列,并缓冲在同一事件循环中产生的所有数据扭转。如果同一个watcher被屡次触发,只会被推入到队列中一次。这种在缓冲时去除反复数据对于防止不必要的计算和DOM操作上十分重要。而后,在下一个的事件循环“tick”中,Vue刷新队列并执行理论 (已去重的) 工作。文言一点就是说,其实这是和JS当中的事件循环是非亲非故的,就是Vue不可能对每一个数据变动都做一次渲染,它会把这些变动先放在一个异步的队列当中,同时它还会对这个队列外面的操作进行去重,比方你批改了这个数据三次,它只会保留最初一次。这些变动是都能够通过队列的模式保存起来,那当初的问题就来到了,那vue是在事件循环的哪个机会来对DOM进行批改呢? Vue有两种抉择,一个是在本次事件循环的最初进行一次DOM更新,另一种是把DOM更新放在下一轮的事件循环当中。z这时,尤雨溪拍了拍胸脯说:“这两种办法,我都有!” 然而因为本轮事件循环最初执行会比放在下一轮事件循环要快很多,所以Vue优先选择第一种,只有当环境不反对的时候才触发第二种机制。(结尾的链接让你懂事件循环) 尽管性能上进步了很多,但这个时候问题就呈现了,咱们都晓得在一轮事件循环中,同步执行栈中代码执行实现之后,才会执行异步队列当中的内容,那咱们获取DOM的操作是一个同步的呀!!那岂不是尽管我曾经把数据改掉了,然而它的更新异步的,而我在获取的时候,它还没有来得及改,所以会呈现文章结尾的那个问题。 这。。。我的确须要进行这样操作,那这么办呢?? 没关系啦,尤大很贴心的为咱们提供了Vue.$nextTick() 参考vue面试题解答 前端vue面试题具体解答 Vue.$nextTick()其实一句话就能够把$nextTick这个货色讲明确:就是你放在$nextTick 当中的操作不会立刻执行,而是等数据更新、DOM更新实现之后再执行,这样咱们拿到的必定就是最新的了。 再精确一点来讲就是$nextTick办法将回调提早到下次DOM更新循环之后执行。(看不懂这句人话的,能够看下面[狗头]) 意思咱们都懂了,那$nextTick是怎么实现这个神奇的性能的呢? 外围如下: Vue在外部对异步队列尝试应用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不反对,则会采纳 setTimeout(fn, 0)代替。认真地看这句话,你就能够发现这不就是利用 JavaScript 的这些异步回调工作队列,来实现 Vue 框架中本人的异步回调队列。这其实就是一个典型的将底层 JavaScript 执行原理利用到具体案例中的示例。 我在这里略微总结一下:就是$nextTick将回调函数放到微工作或者宏工作当中以提早它地执行程序;(总结的也比拟懒) 重要的是了解源码中它的三个参数的意思: callback:咱们要执行的操作,能够放在这个函数当中,咱们没执行一次$nextTick就会把回调函数放到一个异步队列当中;pending:标识,用以判断在某个事件循环中是否为第一次退出,第一次退出的时候才触发异步执行的队列挂载timerFunc:用来触发执行回调函数,也就是Promise.then或MutationObserver或setImmediate 或setTimeout的过程了解之后,在看整个$nextTick外面的执行过程,其实就是把一个个$nextTick中的回调函数压入到callback队列当中,而后依据事件的性质期待执行,轮到它执行的时候,就执行一下,而后去掉callback队列中相应的事件。 应用说了这么多,怎么用它呢? 很简略很简略 mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered })}应用场景created中获取DOM的操作须要应用它就是咱们下面的例子,你如果想要获取最新值,就用它还有一些第三方插件应用过程中,应用到的状况,具体问题具体分析补充之前我始终搞不懂一个的问题,$nextTick既然把它传入的办法变成微工作了,那它和其它微工作的执行程序是怎么的呢? 这简略来说就是谁先挂载Promise对象的问题,在调用$nextTick办法时就会将其闭包外部保护的执行队列挂载到Promise对象,在数据更新时Vue外部首先就会执行$nextTick办法,之后便将执行队列挂载到了Promise对象上,其实在明确Js的Event Loop模型后,将数据更新也看做一个$nextTick办法的调用,并且明确$nextTick办法会一次性执行所有推入的回调,就能够明确执行程序的问题了 还有$nextTick和nextTick区别就是nextTick多了一个context参数,用来指定上下文。但两个的实质是一样的,$nextTick是实例办法,nextTick是类的静态方法而已;实例办法的一个益处就是,主动给你绑定为调用实例的this罢了。

October 4, 2022 · 1 min · jiezi

关于vue.js:vue面试之CompositionAPI响应式包装对象原理

本文次要分以下两个局部对 Composition API 的原理进行解读: reactive API 原理ref API 原理reactive API 原理关上源码能够找到reactive的入口,在composition-api/src/reactivity/reactive.ts,咱们先从函数入口开始剖析reactive产生了什么事件,通过之前的学习咱们晓得,reactive用于创立响应式对象,须要传递一个一般对象作为参数。 export function reactive<T = any>(obj: T): UnwrapRef<T> { if (process.env.NODE_ENV !== 'production' && !obj) { warn('"reactive()" is called without provide an "object".'); // @ts-ignore return; } if (!isPlainObject(obj) || isReactive(obj) || isNonReactive(obj) || !Object.isExtensible(obj)) { return obj as any; } // 创立一个响应式对象 const observed = observe(obj); // 标记一个对象为响应式对象 def(observed, ReactiveIdentifierKey, ReactiveIdentifier); // 初始化对象的访问控制,便于拜访ref属性时主动解包装 setupAccessControl(observed); return observed as UnwrapRef<T>;}首先,在开发环境下,会进行传参测验,如果没有传递对应的obj参数,开发环境下会给予开发者一个正告,在这种状况,为了不影响生产环境,生产环境下会将正告放过。 ...

October 4, 2022 · 4 min · jiezi

关于vue.js:vue为什么vfor的优先级比vif的高

前言有时候有些面试中常常会问到v-for与v-if谁的优先级高,这里就通过剖析源码去解答一下这个问题。 上面的内容是在 当咱们谈及v-model,咱们在探讨什么?的根底上剖析的,所以浏览上面内容之前可先看这篇文章。 持续从编译登程以上面的例子登程剖析: new Vue({ el:'#app', template:` <ul> <li v-for="(item,index) in items" v-if="index!==0"> {{item}} </li> </ul> `})从上篇文章能够晓得,编译有三个步骤 parse : 解析模板字符串生成 AST语法树optimize : 优化语法树,次要时标记动态节点,进步更新页面的性能codegen : 生成js代码,次要是render函数和staticRenderFns函数咱们再次顺着这三个步骤对上述例子进行剖析。 parseparse过程中,会对模板应用大量的正则表达式去进行解析。结尾的例子会被解析成以下AST节点: // 其实ast有很多属性,我这里只展现波及到剖析的属性ast = { 'type': 1, 'tag': 'ul', 'attrsList': [], attrsMap: {}, 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [], 'attrsMap': { 'v-for': '(item,index) in data', 'v-if': 'index!==0' }, // v-if解析进去的属性 'if': 'index!==0', 'ifConditions': [{ 'exp': 'index!==0', 'block': // 指向el本身 }], // v-for解析进去的属性 'for': 'items', 'alias': 'item', 'iterator1': 'index', 'parent': // 指向其父节点 'children': [ 'type': 2, 'expression': '_s(item)' 'text': '{{item}}', 'tokens': [ {'@binding':'item'}, ] ] }]}对于v-for指令,除了记录在attrsMap和attrsList,还会新增for(对应要遍历的对象或数组),alias,iterator1,iterator2对应v-for指令绑定内容中的第一,第二,第三个参数,结尾的例子没有第三个参数,因而没有iterator2属性。 ...

October 4, 2022 · 3 min · jiezi

关于vue.js:js函数式编程讲解

什么是函数式编程是一种编程范型,它将电脑运算视为数学上的函数计算,并且防止应用程序状态以及易变对象。函数式编程更加强调程序执行的后果而非执行的过程,提倡利用若干简略的执行单元让计算结果一直渐进,逐层推导简单的运算,而不是设计一个简单的执行过程。函数式编程的思维过程是齐全不同的,它的着眼点是函数,而不是过程,它强调的是如何通过函数的组合变换去解决问题,而不是我通过写什么样的语句去解决问题为什么叫函数式编程依据学术上函数的定义,函数即是一种形容汇合和汇合之间的转换关系,输出通过函数都会返回有且只有一个输入值。函数实际上是一个关系,或者说是一种映射,而这种映射关系是能够组合的。 在咱们的编程世界中,咱们须要解决的其实也只有“数据”和“关系”,而关系就是函数。咱们所谓的编程工作也不过就是在找一种映射关系,一旦关系找到了,问题就解决了,剩下的事件,就是让数据流过这种关系,而后转换成另一个数据。 函数式编程的特点函数是一等公民。你能够像看待任何其余数据类型一样看待它们——把它们存在数组里,当作参数传递,赋值给变量...等等。应用总有返回值的表达式而不是语句 // 函数式编程-函数作为返回参数const add = (x) => { return plus = (y) => { return x + y; }};let plus1 = add(1);let plus2 = add(2);console.log(plus1(1)); // 2console.log(plus2(1)); // 3申明式编程 (Declarative Programming)不再批示计算机如何工作,而是指出咱们明确心愿失去的后果。与命令式不同,申明式意味着咱们要写表达式,而不是一步一步的批示。 以 SQL 为例,它就没有“先做这个,再做那个”的命令,有的只是一个指明咱们想要从数据库取什么数据的表达式。至于如何取数据则是由它本人决定的。当前数据库降级也好,SQL 引擎优化也好,基本不须要更改查问语句。 无状态和数据不可变 (Statelessness and Immutable data)这是函数式编程的外围概念: 数据不可变: 它要求你所有的数据都是不可变的,这意味着如果你想批改一个对象,那你应该创立一个新的对象用来批改,而不是批改已有的对象。无状态: 次要是强调对于一个函数,不论你何时运行,它都应该像第一次运行一样,给定雷同的输出,给出雷同的输入,齐全不依赖内部状态的变动。// 比拟 Array 中的 slice 和 splicelet test = [1, 2, 3, 4, 5];// slice 为纯函数,返回一个新的数组console.log(test.slice(0, 3)); // [1, 2, 3]console.log(test); // [1, 2, 3, 4, 5]// splice则会批改参数数组console.log(test.splice(0, 3)); // [1, 2, 3]console.log(test); // [4, 5]函数应该纯天然,无副作用纯函数是这样一种函数,即雷同的输出,永远会失去雷同的输入,而且没有任何可察看的副作用。 ...

October 3, 2022 · 2 min · jiezi

关于vue.js:js进阶手写常见函数

JavaScript进阶的必要性无论是学习react还是vue,它们都是js的利用框架。剥去他们的壳子看到的始终是js,所以作为一个前端大厨必须要熟练掌握好js这个大勺,能力烧出一顿好菜 无论是自我晋升还是应酬面试以下这些手写性能是每一个前端程序员必须把握的 1. 手写apply、call、bind 每个Function对象都存在apply()、call()、bind() 办法,其作用都是能够在特定的作用域 中调用函数,等于设置函数体内this对象的值,以裁减函数赖以运行的作用域。apply、call、bind 的异同 1. 三者都能够扭转this的指向,第一个参数都是this,如果指向是null或者undefined则指向window 2. apply的参数是数组,call是列表,而bind能够屡次传入 3. apply和call是扭转this的指向之后间接运行函数,而bind则是返回绑定之后的函数apply实现的参考代码/** * 手写apply */window.name='gy' // 全局变量 let obj={ name:'ckx'}var func=function(b,c){ console.log(`this=`, this) console.log(this.name,b,c) return 1}func('24','hz') // gy 24 hzfunc.apply(obj,['24','hz']) // ckx 24 hzlet newObj={ name:'xmx', age:24}Function.prototype.myApply=function(base,args){ // 1. 如果指向是null 或者undefined 则指向window base=base || window // 2. 依据this是谁调用就指向谁的原理,将this指向的函数 赋值给base对象的一个属性 base.fn=this // 3.执行函数,调用base.fn时,fn中的函数指向 base对象 let result=base.fn(...args) // 4. 删除base的fn属性 delete base.fn // 5. 返回result 后果 return result}func.myApply(newObj,['55','yw']) // xmx 55 ywapply代码执行成果 ...

October 3, 2022 · 3 min · jiezi

关于vue.js:Vue实战必会的几个技巧

键盘事件在 js 中咱们通常通过绑定一个事件,去获取按键的编码,再通过 event 中的 keyCode 属性去取得编码如果咱们须要实现固定的键能力触发事件时就须要一直的判断,其实很麻烦let button = document.querySelector('button')button.onkeyup = function (e) { console.log(e.key) if (e.keyCode == 13) { console.log('我是回车键') }}vue 中给一些罕用的按键提供了别名,咱们只有在事件后加上响应的别名即可vue 中常见别名有:up/向上箭头、down/向下箭头、left/左箭头、right/右箭头、space/空格、tab/换行、esc/退出、enter/回车、delete/删除// 只有按下回车键时才会执行 send 办法<input v-on:keyup.enter="send" type="text">对于 Vue 中未提供别名的键,能够应用原始的 key 值去绑定,所谓 key 值就是 event.key 所取得的值如果 key 值是单个字母的话间接应用即可,如果是由多个单词组成的驼峰命名,就须要将其拆开,用 - 连贯// 只有按下q键时才会执行send办法<input v-on:keyup.Q="send" type="text">// 只有按下capslock键时才会执行send办法<input v-on:keyup.caps-lock="send" type="text">对于零碎修饰符 ctrl、alt、shift 这些比较复杂的键应用而言,分两种状况因为这些键能够在按住的同时,去按其余键,造成组合快捷键当触发事件为 keydown 时,咱们能够间接按下修饰符即可触发当触发事件为 keyup 时,按下润饰键的同时要按下其余键,再开释其余键,事件能力被触发。// keydown事件时按下alt键时就会执行send办法<input v-on:keydown.Alt="send" type="text">// keyup事件时须要同时按下组合键才会执行send办法<input v-on:keyup.Alt.y="send" type="text">当然咱们也能够自定义按键别名通过 Vue.config.keyCodes.自定义键名=键码 的形式去进行定义// 只有按下回车键时才会执行send办法<input v-on:keydown.autofelix="send" type="text">// 13是回车键的键码,将他的别名定义为autofelixVue.config.keyCodes.autofelix=13 图片预览在我的项目中咱们常常须要应用到图片预览,viewerjs 是一款十分炫酷的图片预览插件性能反对包含图片放大、放大、旋转、拖拽、切换、拉伸等装置 viewerjs 扩大npm install viewerjs --save引入并配置性能//引入import Vue from 'vue';import 'viewerjs/dist/viewer.css';import Viewer from 'v-viewer';//按需引入Vue.use(Viewer);Viewer.setDefaults({ 'inline': true, 'button': true, //右上角按钮 "navbar": true, //底部缩略图 "title": true, //以后图片题目 "toolbar": true, //底部工具栏 "tooltip": true, //显示缩放百分比 "movable": true, //是否能够挪动 "zoomable": true, //是否能够缩放 "rotatable": true, //是否可旋转 "scalable": true, //是否可翻转 "transition": true, //应用 CSS3 适度 "fullscreen": true, //播放时是否全屏 "keyboard": true, //是否反对键盘 "url": "data-source", ready: function (e) { console.log(e.type, '组件以初始化'); }, show: function (e) { console.log(e.type, '图片显示开始'); }, shown: function (e) { console.log(e.type, '图片显示完结'); }, hide: function (e) { console.log(e.type, '图片暗藏实现'); }, hidden: function (e) { console.log(e.type, '图片暗藏完结'); }, view: function (e) { console.log(e.type, '视图开始'); }, viewed: function (e) { console.log(e.type, '视图完结'); // 索引为 1 的图片旋转20度 if (e.detail.index === 1) { this.viewer.rotate(20); } }, zoom: function (e) { console.log(e.type, '图片缩放开始'); }, zoomed: function (e) { console.log(e.type, '图片缩放完结'); }})应用图片预览插件单个图片应用<template> <div> <viewer> <img :src="cover" style="cursor: pointer;" height="80px"> </viewer> </div></template><script>export default { data() { return { cover: "//www.autofelix.com/images/cover.png" } }}</script>多个图片应用<template> <div> <viewer :images="imgList"> <img v-for="(imgSrc, index) in imgList" :key="index" :src="imgSrc" /> </viewer> </div></template><script>export default { data() { return { imgList: [ "//www.autofelix.com/images/pic_1.png", "//www.autofelix.com/images/pic_2.png", "//www.autofelix.com/images/pic_3.png", "//www.autofelix.com/images/pic_4.png", "//www.autofelix.com/images/pic_5.png" ] } }}</script>相干vue实战视频解说:进入学习 跑马灯这是一款好玩的特效技巧比方你在机场接人时,能够应用手机跑马灯特效,成为人群中最靓的仔跑马灯特效其实就是将最后面的文字删除,增加到最初一个,这样就造成了文字挪动的成果<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>跑马灯</title> <style type="text/css"> #app { padding: 20px; } </style></head><body> <div id="app"> <button @click="run">应援</button> <button @click="stop">暂停</button> <h3>{{ msg }}</h3> </div></body><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script> new Vue({ el: "#app", data: { msg: "飞兔小哥,飞兔小哥,我爱飞兔小哥~~~", timer: null // 定时器 }, methods: { run() { // 如果timer曾经赋值就返回 if (this.timer) return; this.timer = setInterval(() => { // msg宰割为数组 var arr = this.msg.split(''); // shift删除并返回删除的那个,push增加到最初 // 把数组第一个元素放入到最初面 arr.push(arr.shift()); // arr.join('')吧数组连贯为字符串复制给msg this.msg = arr.join(''); }, 100) }, stop() { //革除定时器 clearInterval(this.timer); //革除定时器之后,须要从新将定时器置为null this.timer = null; } } })</script></html> 倒计时对于倒计时技巧,利用的中央很多比方很多抢购商品的时候,咱们须要有一个倒计时揭示用户开抢工夫其实就是每隔一秒钟,去从新计算一下工夫,并赋值到 DOM 中<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>倒计时</title></head><body> <div id="app"> <div>抢购开始工夫:{{count}}</div> </div></body><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script><script> new Vue({ el: "#app", data() { return { count: '', //倒计时 seconds: 864000 // 10天的秒数 } }, mounted() { this.Time() //调用定时器 }, methods: { // 天 时 分 秒 格式化函数 countDown() { let d = parseInt(this.seconds / (24 * 60 * 60)) d = d < 10 ? "0" + d : d let h = parseInt(this.seconds / (60 * 60) % 24); h = h < 10 ? "0" + h : h let m = parseInt(this.seconds / 60 % 60); m = m < 10 ? "0" + m : m let s = parseInt(this.seconds % 60); s = s < 10 ? "0" + s : s this.count = d + '天' + h + '时' + m + '分' + s + '秒' }, //定时器没过1秒参数减1 Time() { setInterval(() => { this.seconds -= 1 this.countDown() }, 1000) }, } })</script></html> 自定义右键菜单在我的项目中,咱们有时候须要自定义鼠标右键呈现的选项,而不是浏览器默认的右键选项对于如何实现右键菜单,在 Vue 中其实很简略,只有应用 vue-contextmenujs 插件即可装置 vue-contextmenujs 插件npm install vue-contextmenujs引入//引入import Vue from 'vue';import Contextmenu from "vue-contextmenujs"Vue.use(Contextmenu);应用办法能够应用 <i class="icon"></i> 能够给选项增加图标能够应用 style 标签自定义选项的款式能够应用 disabled 属性禁止选项能够点击能够应用 divided:true 设置选项的下划线能够应用 children 设置子选项<style> .custom-class .menu_item__available:hover, .custom-class .menu_item_expand { background: lightblue !important; color: #e65a65 !important; }</style><template> <div style="width:100vw;height:100vh" @contextmenu.prevent="onContextmenu"></div></template><script> import Vue from 'vue' import Contextmenu from "vue-contextmenujs" Vue.use(Contextmenu); export default { methods: { onContextmenu(event) { this.$contextmenu({ items: [ { label: "返回", onClick: () => { // 增加点击事件后的自定义逻辑 } }, { label: "后退", disabled: true }, { label: "重载", divided: true, icon: "el-icon-refresh" }, { label: "打印", icon: "el-icon-printer" }, { label: "翻译", divided: true, minWidth: 0, children: [{ label: "翻译成中文" }, { label: "翻译成英文" }] }, { label: "截图", minWidth: 0, children: [ { label: "截取局部", onClick: () => { // 增加点击事件后的自定义逻辑 } }, { label: "截取全屏" } ] } ], event, // 鼠标事件信息 customClass: "custom-class", // 自定义菜单 class zIndex: 3, // 菜单款式 z-index minWidth: 230 // 主菜单最小宽度 }); return false; } } };</script> 打印性能对于网页反对打印性能,在很多我的项目中也比拟常见而 Vue 中应用打印性能,能够应用 vue-print-nb 插件装置 vue-print-nb 插件npm install vue-print-nb --save引入打印服务import Vue from 'vue'import Print from 'vue-print-nb'Vue.use(Print);应用应用 v-print 指令即可启动打印性能<div id="printStart"> <p>红酥手,黄縢酒,满城秋色宫墙柳。</p> <p>东风恶,欢情薄。</p> <p>一怀愁绪,几年离索。</p> <p>错、错、错。</p> <p>春如旧,人空瘦,泪痕红浥鲛绡透。</p> <p>桃花落,闲池阁。</p> <p>山盟虽在,锦书难托。</p> <p>莫、莫、莫!</p></div><button v-print="'#printStart'">打印</button> JSONP申请jsonp 是 解决跨域 的次要形式之一所以学会在 vue 中应用 jsonp 其实还是很重要的装置 jsonp 扩大npm install vue-jsonp --save-dev注册服务// 在vue2中注册服务import Vue from 'vue'import VueJsonp from 'vue-jsonp'Vue.use(VueJsonp)// 在vue3中注册服务import { createApp } from 'vue'import App from './App.vue'import VueJsonp from 'vue-jsonp'createApp(App).use(VueJsonp).mount('#app')应用办法须要留神的是,在应用 jsonp 申请数据后,回调并不是在 then 中执行而是在自定义的 callbackName 中执行,并且须要挂载到 window 对象上<script>export default { data() {...}, created() { this.getUserInfo() }, mounted() { window.jsonpCallback = (data) => { // 返回后回调 console.log(data) } }, methods: { getUserInfo() { this.$jsonp(this.url, { callbackQuery: "callbackParam", callbackName: "jsonpCallback" }) .then((json) => { // 返回的jsonp数据不会放这里,而是在 window.jsonpCallback console.log(json) }) } } }</script>

October 3, 2022 · 4 min · jiezi

关于vue.js:Vue响应式依赖收集原理分析vue高级必备

背景在 Vue 的初始化阶段,_init 办法执行的时候,会执行 initState(vm) ,它的定义在 src/core/instance/state.js 中。在初始化 data 和 props option 时咱们留神 initProps 和 initData 办法中都调用了 observe 办法。通过 observe (value),就能够将数据变成响应式。 export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) }}initProps ...

October 3, 2022 · 9 min · jiezi

关于vue.js:elementui源码学习之仿写一个eldrawer

本篇文章记录仿写一个el-drawer组件细节,从而有助于大家更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在github上,大家能够拉下来,npm start运行跑起来,联合正文有助于更好的了解。github仓库地址如下:https://github.com/shuirongsh...什么是抽屉drawer组件同弹框dialog组件相似,UI展现略有不同个别抽屉是左右防线弹出和发出,高低方向不多可在抽屉外部进行代码补充操作某些状况下,抽屉组件比弹框组件更加好用一些笔者对于抽屉组件的封装,就不写太多的解析阐明了,大家能够间接复制粘贴代码,搭配代码中的正文进行应用(联合本人公司业务封装) 笔者的抽屉组件实现,抛砖引玉。实现次要罕用的性能,道友们能够进行思维发散效果图先看一下抽屉组件的效果图 代码应用时的代码<template> <div> <h4>isShowDrawer.sync属性管制是否显示抽屉</h4> <h4>title属性管制抽屉的头部题目</h4> <h4>direction属性管制抽屉的4个方向</h4> <h4>beforeClose函数属性敞开抽屉前的操作动作</h4> <h4>showCloseIcon属性管制是否显示抽屉的敞开小按钮</h4> <h4>isShowHeader属性管制是否显示抽屉的头部内容</h4> <h4>mask属性管制是否显示抽屉的背景遮罩层</h4> <h4>slot="title"具名插槽管制头部的题目内容</h4> <h4>clickMaskClose属性管制是否可能点击背景遮罩层敞开抽屉</h4> <br /> <my-drawer :isShowDrawer.sync="isShowDrawer1" title="上方弹出direction='top'" direction="top" :beforeClose="handleClose" :showCloseIcon="false" ></my-drawer> <my-drawer :isShowDrawer.sync="isShowDrawer2" title="下方弹出" direction="bottom" :isShowHeader="false" > <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> <h1>:isShowHeader="false"去掉抽屉的头部内容</h1> </my-drawer> <my-drawer :isShowDrawer.sync="isShowDrawer3" direction="left" :mask="false" > <span slot="title">左侧命名插槽弹出哦^_^</span> <span>没有背景遮罩层</span> </my-drawer> <my-drawer :isShowDrawer.sync="isShowDrawer4" direction="right" :clickMaskClose="false" > <span slot="title">右侧命名插槽弹出哦^_^</span> <span>设置点击背景遮罩层不敞开,只能点击小箭头,或自定义按钮敞开</span> <br /> <br /> <br /> <br /> <el-button @click="isShowDrawer4 = false" type="success" size="small" plain >自定义敞开</el-button > </my-drawer> <el-button @click="topOpen" type="success" plain>上方弹出</el-button> <el-button @click="bottomOpen" type="success" plain>下方弹出</el-button> <el-button @click="leftOpen" type="success" plain>左侧弹出</el-button> <el-button @click="rightOpen" type="success" plain>右侧弹出</el-button> </div></template><script>export default { data() { return { isShowDrawer1: false, isShowDrawer2: false, isShowDrawer3: false, isShowDrawer4: false, }; }, methods: { topOpen() { this.isShowDrawer1 = true; }, bottomOpen() { this.isShowDrawer2 = true; }, leftOpen() { this.isShowDrawer3 = true; }, rightOpen() { this.isShowDrawer4 = true; }, handleClose(close) { this.$confirm("确认敞开close()函数敞开") .then((_) => { close(); }) .catch((_) => {}); }, },};</script>封装的抽屉组件代码<template> <!-- 抽屉关上敞开过渡成果依据name去指定 --> <transition :name="computedName"> <!-- clickMaskCloseFn搭配@click.stop --> <div @click="clickMaskCloseFn" class="myDrawerWrap" :class="{ isShowDrawerMask: mask }" v-show="isShowDrawer" > <div ref="drawerContentRef" :class="['drawerContent']" :style="computedDrawerPosition" @click.stop > <header v-show="isShowHeader" class="drawerHeader"> <slot name="title"> <span>{{ title }}</span> </slot> <i class="el-icon-close" @click="closeDrawer" v-show="showCloseIcon"> </i> </header> <section class="drawerBody"> <slot></slot> </section> </div> </div> </transition></template><script>const directionArr = ["top", "bottom", "left", "right"]; // "ttb","btt","ltr","rtl"const moveObj = { top: "topMove", bottom: "bottomMove", left: "leftMove", right: "rightMove",};export default { name: "myDrawer", props: { // 是否显示抽屉 isShowDrawer: { type: Boolean, default: false, }, // 是否显示抽屉头部内容 isShowHeader: { type: Boolean, default: true, }, // 父组件传过来的抽屉题目值 title: { type: String, default: "我是title", }, // 是否显示敞开小图标 showCloseIcon: { type: Boolean, default: true, }, // 是否开启抽屉背景遮罩层 mask: { type: Boolean, default: true, }, // 点击遮罩层敞开默认为true clickMaskClose: { type: Boolean, default: true, }, // 校验抽屉的4个方向 direction: { type: String, default: "right", validator(val) { return directionArr.includes(val); }, }, // 接管父组件传递过去的敞开函数,会中断敞开抽屉的操作 beforeClose: { type: Function, }, }, computed: { // 动态控制上下左右的抽屉内容区的地位以及抽屉的宽度 computedDrawerPosition() { let positionObj = { width: (this.direction == "left") | (this.direction == "right") ? "30%" : "100%", height: (this.direction == "top") | (this.direction == "bottom") ? "30%" : "100%", }; positionObj[this.direction] = 0; return positionObj; }, // 动态控制抽屉从上下左右进入和退出 computedName() { return moveObj[this.direction]; // topMove、bottomMove、leftMove、rightMove }, }, methods: { // 点击遮罩层敞开弹框 clickMaskCloseFn() { if (this.clickMaskClose == true) { this.closeDrawer(); } else { /* 这里要管制一下冒泡事件,留神第十行应用@click.stop 不管制冒泡的话,点击内容区也会导致弹出框敞开*/ return; } }, // 筹备敞开抽屉弹出框 closeDrawer() { console.log(888); // 若传递了beforeClose函数,就抛出敞开函数,供内部应用 if (this.beforeClose) { this.beforeClose(this.close); } // 没有beforeClose函数,间接敞开即可 else { this.close(); } }, // 敞开抽屉弹出框 close() { this.$emit("update:isShowDrawer", false); // 敞开 this.$emit("shutDown"); // 并抛出一个shutDown告诉事件 }, },};</script><style lang='less' scoped>// 根本款式.myDrawerWrap { position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 999; overflow: hidden; .drawerContent { // 搭配定位的形式管制在上下左右的那个方位 position: absolute; background-color: #fff; box-shadow: 2px 2px 12px 0 rgba(0, 0, 0, 0.24); display: flex; flex-direction: column; // 抽屉头部 .drawerHeader { width: 100%; height: 48px; box-sizing: border-box; padding: 12px; display: flex; align-items: center; justify-content: space-between; font-weight: bolder; color: #333; i { cursor: pointer; } } // 抽屉内容体局部 .drawerBody { width: 100%; box-sizing: border-box; padding: 12px; flex: 1; overflow-y: auto; } }}// 遮罩层即为背景色.isShowDrawerMask { background-color: rgba(0, 0, 0, 0.3);}/* 下方是抽屉过渡动画的重点*/// 上方进入和退出.topMove-enter-active,.topMove-leave-active { transition: all 0.36s ease-in-out; transform: translateY(0%); opacity: 1;}.topMove-enter,.topMove-leave { transform: translateY(-100%); opacity: 0;}.topMove-leave-to { transform: translateY(-100%); opacity: 0;}// 下方进入和退出.bottomMove-enter-active,.bottomMove-leave-active { transition: all 0.36s ease-in-out; transform: translateY(0); opacity: 1;}.bottomMove-enter,.bottomMove-leave { transform: translateY(100%); opacity: 0;}.bottomMove-leave-to { transform: translateY(100%); opacity: 0;}// 左侧进入和退出.leftMove-enter-active,.leftMove-leave-active { transition: all 0.36s ease-in-out; transform: translateX(0%); opacity: 1;}.leftMove-enter,.leftMove-leave { transform: translateX(-100%); opacity: 0;}.leftMove-leave-to { transform: translateX(-100%); opacity: 0;}// 右侧进入和退出.rightMove-enter-active,.rightMove-leave-active { transition: all 0.36s ease-in-out; transform: translateX(0); opacity: 1;}.rightMove-enter,.rightMove-leave { transform: translateX(100%); opacity: 0;}.rightMove-leave-to { transform: translateX(100%); opacity: 0;}</style>总结A bad pen is better than a good memory残缺代码在github上哦(还有其余笔者封装的组件)

October 3, 2022 · 3 min · jiezi

关于vue.js:深度学习Vue源码模板编译原理

前言此篇次要手写 Vue2.0 源码-模板编译原理 上一篇咱们次要介绍了 Vue 数据的响应式原理 对于中高级前端来说 响应式原理根本是面试 Vue 必考的源码根底类 如果不是很分明的话根本就被 pass 了 那么明天咱们手写的模板编译原理也是 Vue 面试比拟频繁的一个点 而且复杂程度是高于响应式原理的 外面次要波及到 ast 以及大量正则匹配 大家学习完能够看着思维导图一起手写一遍加深印象哈 适用人群: 没工夫去看官网源码或者看源码看的比拟懵而不想去看的同学 注释// Vue实例化new Vue({ el: "#app", data() { return { a: 111, }; }, // render(h) { // return h('div',{id:'a'},'hello') // }, // template:`<div id="a">hello</div>`});下面这段代码 大家肯定不生疏 依照官网给出的生命周期图 咱们传入的 options 选项外面能够手动配置 template 或者是 render 留神一:平时开发中 咱们应用的是不带编译版本的 Vue 版本(runtime-only)间接在 options 传入 template 选项 在开发环境报错 留神二:这里传入的 template 选项不要和.vue 文件外面的<template>模板搞混同了 vue 单文件组件的 template 是须要 vue-loader 进行解决的 ...

October 3, 2022 · 5 min · jiezi

关于vue.js:上帝视角看Vue源码整体架构相关源码问答

前言这段时间利用课余时间夹杂了很多很多事把 Vue2 源码学习了一遍,但很多都是跟着视频大略过了一遍,也都画了本人的思维导图。但还是对详情的感怀模糊不清,故这段时间对源码进行了总结梳理。 本篇文章更适合于已看过 Vue2 源码,进一步总结加深概念的人群。若还未读过源码或系统只知其一;不知其二的小伙伴,也能够筛选阶段进行总结梳理,集体还是强烈认为须要过一遍源码。目录构造├── benchmarks 性能、基准测试├── dist 构建打包的输入目录├── examples 案例目录├── flow flow 语法的类型申明├── packages 一些额定的包,比方:负责服务端渲染的包 vue-server-renderer、配合 vue-loader 应用的的 vue-template-compiler,还有 weex 相干的│ ├── vue-server-renderer│ ├── vue-template-compiler│ ├── weex-template-compiler│ └── weex-vue-framework├── scripts 所有的配置文件的寄存地位,比方 rollup 的配置文件├── src vue 源码目录│ ├── compiler 编译器│ ├── core 运行时的外围包│ │ ├── components 全局组件,比方 keep-alive│ │ ├── config.js 一些默认配置项│ │ ├── global-api 全局 API,比方相熟的:Vue.use()、Vue.component() 等│ │ ├── instance Vue 实例相干的,比方 Vue 构造函数就在这个目录下│ │ ├── observer 响应式原理│ │ ├── util 工具办法│ │ └── vdom 虚构 DOM 相干,比方相熟的 patch 算法就在这儿│ ├── platforms 平台相干的编译器代码│ │ ├── web│ │ └── weex│ ├── server 服务端渲染相干├── test 测试目录├── types TS 类型申明Vue 初始化地位:/src/core/instance/index.js入口// Vue 的构造函数function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } // 在 /src/core/instance/init.js, // 1.初始化组件实例关系属性 // 2.自定义事件的监听 // 3.插槽和渲染函数 // 4.触发 beforeCreate 钩子函数 // 5.初始化 inject 配置项 // 6.初始化响应式数据,如 props, methods, data, computed, watch // 7.初始化解析 provide // 8.触发 created 钩子函数 this._init(options)}外围代码源码外围代码程序以深度遍历模式 ...

October 3, 2022 · 24 min · jiezi

关于vue.js:面试官vue2和vue3的区别有哪些

一、Vue3 与 Vue2 区别详述1. 生命周期对于生命周期来说,整体上变动不大,只是大部分生命周期钩子名称上 + “on”,性能上是相似的。不过有一点须要留神,Vue3 在组合式API(Composition API,上面开展)中应用生命周期钩子时须要先引入,而 Vue2 在选项API(Options API)中能够间接调用生命周期钩子,如下所示。 // vue3<script setup> import { onMounted } from 'vue'; // 应用前需引入生命周期钩子onMounted(() => { // ...});// 可将不同的逻辑拆开成多个onMounted,仍然按程序执行,不会被笼罩onMounted(() => { // ...});</script>// vue2<script> export default { mounted() { // 间接调用生命周期钩子 // ... }, }</script> 罕用生命周期比照如下表所示。 vue2vue3beforeCreate created beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeDestroyonBeforeUnmountdestroyedonUnmountedTips: setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不须要显式地去定义。2. 多根节点相熟 Vue2 的敌人应该分明,在模板中如果应用多个根节点时会报错,如下所示。 // vue2中在template里存在多个根节点会报错<template> <header></header> <main></main> <footer></footer></template>// 只能存在一个根节点,须要用一个<div>来包裹着<template> <div> <header></header> <main></main> <footer></footer> </div></template>然而,Vue3 反对多个根节点,也就是 fragment。即以下多根节点的写法是被容许的。 ...

October 3, 2022 · 6 min · jiezi

关于vue.js:写过自定义指令吗原理是什么

背景看了一些自定义指令的文章,然而探索其原理的文章却不多见,所以我决定水一篇。 如何自定义指令?其实对于这个问题官网文档上曾经有了很好的示例的,咱们先来温故一下。 除了外围性能默认内置的指令 (v-model 和 v-show),Vue 也容许注册自定义指令。留神,在 Vue2.0 中,代码复用和形象的次要模式是组件。然而,有的状况下,你依然须要对一般 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下: 当页面加载时,该元素将取得焦点 (留神: autofocus 在挪动版 Safari 上不工作)。事实上,只有你在关上这个页面后还没点击过任何内容,这个输入框就该当还是处于聚焦状态。当初让咱们用指令来实现这个性能: // 注册一个全局自定义指令 `v-focus`Vue.directive('focus', {// 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() }})如果想注册部分指令,组件中也承受一个 directives 的选项: directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() } }}而后你能够在模板中任何元素上应用新的 v-focus property,如下: <input v-focus>指令外部提供的钩子函数一个指令定义对象能够提供如下几个钩子函数 (均为可选): bind: 只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。inserted: 被绑定元素插入父节点时调用 (仅保障父节点存在,但不肯定已被插入文档中)。update: 所在组件的 VNode 更新时调用,然而可能产生在其子 VNode 更新之前。指令的值可能产生了扭转,也可能没有。然而你能够通过比拟更新前后的值来疏忽不必要的模板更新 (具体的钩子函数参数见下)。componentUpdated: 指令所在组件的 VNode 及其子VNode全副更新后调用。unbind: 只调用一次,指令与元素解绑时调用。钩子函数参数指令钩子函数会被传入以下参数: el: 指令所绑定的元素,能够用来间接操作 DOM 。binding: 一个对象,蕴含以下 property:name:指令名,不包含 v- 前缀。value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否扭转都可用。expression:字符串模式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。arg: 传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。modifiers:一个蕴含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。vnode: Vue 编译生成的虚构节点。能够参考官网的 VNode API 来理解更多详情。oldVnode:上一个虚构节点,仅在 update 和 componentUpdated 钩子中可用。除了 el 之外,其它参数都应该是只读的,切勿进行批改。如果须要在钩子之间共享数据,倡议通过元素的 dataset 来进行。 ...

October 3, 2022 · 4 min · jiezi

关于vue.js:VUE自定义事件传递默认参数同时传入自定义参数

最近在开发时遇到一个问题,应用 element-ui 组件库的 input-number 组件时,自定义其 @change 事件,@change 事件默认传入 newVal, oldVal 两个参数,能够用如下写法 <el-input-number @change="numberChanged" />methods: { numberChanged(newVal, oldVal) { console.log(newVal, oldVal); },但如果此时,除默认参数 newVal, oldVal 外,须要再传递一个自定义参数 ar1 该如何解决。首先思考到应用 $event, <el-input-number @change="numberChanged(ar1, $event)" />methods: { numberChanged(ar1, e) { console.log(ar1, e); },但此时 e=newVal,即默认的第一个参数,默认的第二个参数 oldVal 仍无奈传递。 网上也没有找到相应解决办法,VUE自定义事件中,对于子组件传递多个参数的同时,父组件又要传递额定的参数的实际 给出了应用对象传递的办法,行将第一个默认参数和第二个默认参数合并成一个对象,只传递一个默认参数,但该办法不仅繁琐,而且局限性较大。 明天突发奇想到一个新的办法,如下: <el-input-number @change="(newVal, oldVal) => { numberChanged(ar1, newVal, oldVal); }" />methods: { numberChanged(ar1, newVal, oldVal) { console.log(ar1, newVal, oldVal); },相较之下显得简洁的多,故在此记录,供大家参考,也心愿集思广益,看有无新的更简洁的办法。

October 2, 2022 · 1 min · jiezi

关于vue.js:OpenDataV低代码平台增加自定义属性编辑

上一篇咱们讲到了怎么在OpenDataV中增加本人的组件,为了让大家更快的上手咱们的平台,这一次针对自定义属性编辑,咱们再来加一篇阐明。咱们先来看一下OpenDataV中的属性编辑性能。 当咱们拖动一个组件到画布中当前,点击组件,在页面的右侧就呈现了对应的属性编辑。在上一篇新增组件的文章中咱们有一个配置文件config.ts,配置了组件的款式和属性批改,其中对于每一项配置咱们设置了类型FormType,就像如下: 目前咱们的FormType只反对几种固定的形式,这里所配置的类型就会反馈到属性编辑框中,例如FormType.COLOR,就会是一个色彩抉择框,FormType.SWITCH就是一个开关按钮,那如果须要用到的编辑形式在FormType外面不反对怎么办呢?咱们提供了FormType.CUSTOM自定义属性编辑类型,这样就能够针对咱们本人的组件来定制属性编辑框。上面我带大家一步步实现一个自定义的属性编辑框。咱们以ScrollTable组件为例 减少文件在Table/ScrollTable目录下减少vue文件xxx.vue,名称能够随便定义,内容如下: <template> <n-form :model="formData" size="small" label-placement="left"> <n-form-item label="行高度"> <n-input-number v-model:value="formData.height" @keypress.enter.prevent="changeData" /> </n-form-item> <n-form-item label="背景色"> <div class="backcolor"> <n-color-picker v-model:value="formData.oddRowBGC" @complete="changeData" /> <span class="title">奇数行</span> </div> <div class="backcolor"> <n-color-picker v-model:value="formData.evenRowBGC" @complete="changeData" /> <span class="title">偶数行</span> </div> </n-form-item> </n-form></template><script lang="ts" setup>......</script>对于自定义属性编辑组件的书写要求有以下几个: 组件须要接管一个value(必须)和args(可选)属性组件必须给父组件提供updateValue办法组件解决<script lang="ts" setup>import { reactive } from 'vue'import { NForm, NFormItem, NInputNumber, NColorPicker } from 'naive-ui'import { RowType } from './type'const props = defineProps<{ value: RowType args: any}>()const emits = defineEmits<{ (e: 'change', value: RowType)}>()const formData = reactive<RowType>({ height: props.value.height || 30, oddRowBGC: props.value.oddRowBGC || '#003B51', evenRowBGC: props.value.evenRowBGC || '#0A2732'})const changeData = () => { emits('change', formData)}</script>value属性接管的是自定义编辑框的值,和一般的属性一样,然而这里能够接管任意的数据,数组、对象或者根底类型数据,咱们在渲染右侧属性编辑框的时候,会把属性框中的数值通过此属性传递给以后组件。 ...

October 1, 2022 · 1 min · jiezi

关于vue.js:能不能手写Vue响应式前端面试进阶

Vue 视图更新原理Vue 的视图更新原理次要波及的是响应式相干API Object.defineProperty 的应用,它的作用是为对象的某个属性对外提供 get、set 办法,从而实现内部对该属性的读和写操作时可能被外部监听,实现后续的同步视图更新性能 一、实现响应式的外围API:Object.definePropertyObject.defineProperty的用法介绍:MDN-Object.defineProperty,上面是模仿 Vue data 值的更新对API接口进行初步理解 // 模仿 Vue 中的 dataconst data = {}// 对外不可见的外部变量let _myName = 'Yimwu'// 响应式监听 data 中的 nameObject.defineProperty(data, "name", { // 应用 data.name 时 get 办法被调用,返回外部存储变量值 get: () => { console.log('get') return _myName }, // 应用 data.name = xxx 批改变量时,set 办法被调用,设置外部存储变量值 set: (newVal) => { console.log('set') _myName = newVal }})console.log(data.name) // 输入 Yimwu getdata.name = 'Mr.Wu' // 输入 set (监听胜利)二、视图更新初步实现1、updateView为了不便 模仿视图更新,这里创立了一个函数 updateView ,当数据更新时,调用 updateView ,模仿进行了视图更新(在 Vue 中体现为 template 模板中援用了该变量值的 DOM 元素的变动) ...

October 1, 2022 · 3 min · jiezi

关于vue.js:在vue的vfor中key为什么不能用index

写在后面在前端中,次要波及的基本上就是 DOM的相干操作 和 JS,咱们都晓得 DOM 操作是比拟耗时的,那么在咱们写前端相干代码的时候,如何缩小不必要的 DOM 操作便成了前端优化的重要内容。 虚构DOM(virtual DOM)在 jQuery 时代,基本上所有的 DOM 相干的操作都是由咱们本人编写(当然博主是没有写过 jQuery 滴,可能因为博主太年老了吧,错过了 jQuery 大法的时代),如何操作 DOM, 操作 DOM 的机会应该如何安顿成了决定性能的要害,而到了 Vue、React 这些框架流行的时代,框架采纳数据驱动视图,封装了大量的 DOM 操作细节,使得更多的 DOM 操作细节的优化从开发者本人抉择、管制转移到了框架外部,那么在学会应用框架后,如果想要更加深刻学习框架,那就须要搞懂框架封装的底层原理,其中十分外围的一部分就是虚构DOM(virtual DOM) 什么是虚构 DOM简而言之,就是通过 JS 来模仿 DOM 构造,对于纠结以什么 JS 数据结构来模仿 DOM 并没有一套规范,只有能齐全笼罩 DOM 的所有构造即可,上面以较为通用的形式演示一下。 通过对 DOM 构造的剖析,咱们能够用 tag 示意 DOM 节点的类型,props 示意 DOM 节点的所有属性,包含 style、class 等,children 示意子节点(没有子节点则示意内容),这样子咱们就把整个 DOM 通过 JS 模仿进去了,而后呢? 而后看看下一章~~~ // DOM<div class="container"> <h1 style="color: black;" class="title">HeiHei~~</h1> <div class="inner-box"> <span class="myname">I am Yimwu</span> </div></div>// VDOMlet vdom = { tag: 'div', props: { classname: 'container', }, children: [ { tag: 'h1', props: { classname: 'title', style: { color: 'black' } }, children: 'HeiHei~~' }, { tag: 'div', props: { classname: 'inner-box', }, children: [ { tag: 'span', props: { classname: 'myname' }, children: 'I am Yimwu' } ] } ]}虚构 DOM 的作用当咱们可能在 JS 中模拟出 DOM 构造后,咱们就能够通过 JS 来对 DOM 操作进行优化了,怎么优化呢,这个时候 diff 算法就该退场了。当咱们通过 JS 对 DOM 进行批改后,并不会间接触发 DOM 更新,而是会学生成一个新的虚构 DOM,而后利用 diff 算法与批改前生成的虚构 DOM 进行比拟,找出须要批改的点,最初进行真正的 DOM 更新操作 ...

October 1, 2022 · 2 min · jiezi

关于vue.js:vue项目性能优化前端加分项

前言Vue 框架通过数据双向绑定和虚构 DOM 技术,帮咱们解决了前端开发中最脏最累的 DOM 操作局部, 咱们不再须要去思考如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 我的项目中依然存在我的项目首屏优化、Webpack 编译配置优化等问题,所以咱们依然须要去关注 Vue 我的项目性能方面的优化,使我的项目具备更高效的性能、更好的用户体验。本文是作者通过理论我的项目的优化实际进行总结而来,心愿读者读完本文,有肯定的启发思考,从而对本人的我的项目进行优化起到帮忙。本文内容分为以下三局部组成: Vue 代码层面的优化; webpack 配置层面的优化; 根底的 Web 技术层面的优化。 一、代码层面的优化1.1、v-if 和 v-show 辨别应用场景 v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 v-show 就简略得多, 不论初始条件是什么,元素总是会被渲染,并且只是简略地基于 CSS 的 display 属性进行切换。 所以,v-if 实用于在运行时很少扭转条件,不须要频繁切换条件的场景;v-show 则实用于须要十分频繁切换条件的场景。 1.2、computed 和 watch 辨别应用场景 computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值产生扭转,下一次获取 computed 的值时才会从新计算 computed 的值; watch: 更多的是「察看」的作用,相似于某些数据的监听回调 ,每当监听的数据变动时都会执行回调进行后续操作; 使用场景: 当咱们须要进行数值计算,并且依赖于其它数据时,应该应用 computed,因为能够利用 computed 的缓存个性,防止每次获取值时,都要从新计算; 当咱们须要在数据变动时执行异步或开销较大的操作时,应该应用 watch,应用 watch 选项容许咱们执行异步操作 ( 拜访一个 API ),限度咱们执行该操作的频率,并在咱们失去最终后果前,设置中间状态。这些都是计算属性无奈做到的。 1.3、v-for 遍历必须为 item 增加 key,且防止同时应用 v-if ...

September 30, 2022 · 4 min · jiezi

关于vue.js:vue-项目vueconfigjs-中-devServer-proxy-未生效问题解决方案

clone vue-material-admin 我的项目到本地运行, 批改了 .env.development, 心愿代理到本地5005端口 # base apiVUE_APP_BASE_API = 'http://local.vma.isocked.com/api'改为了(参考原始内容照葫芦画瓢, .env.development误人啊) # base apVUE_APP_BASE_API = 'http://127.0.0.1:5005/api/v1/manage'devServer配置代理如下 // devserver 配置如下 devServer: { host: '127.0.0.1', port: 8000, proxy: { '/api': { target: '127.0.0.1:5005/', ws: false, changeOrigin: true, pathRewrite: { // '/api/': '/api/', }, }, }, },但申请时并未通过devServer proxy进行代理, 间接跨域到了5005端口的服务 在查阅了各种复制黏贴的文档后, 终于找到了起因: const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // api base_url timeout: 50000, // timeout, headers: { 'Access-Control-Allow-Origin': '*' },})proxy的代理是依据请url求来判断是否应用代理的, 以上axios service 创立时的baseURL 应用的是 http://127.0.0.1:5005/api/v1/manage, 天然无奈匹配/api的规定, 因而进行如下改变即可 ...

September 29, 2022 · 1 min · jiezi

关于vue.js:撸了一款-Vue-生态缺失的-CMDK-类库

9月底,新轮子又来了,Vue Command Palette 是一个为 Vue 而生的疾速、无款式、可组合的 Command Palette(CMDK)组件库。 灵感起源这个组件的诞生的灵感来自上个月察看到一个比拟火的 React 类库我的项目 cmdk cmdk 是一个为 React 而生的疾速、无款式、可组合的 CMDK 组件,由 Linear 工程师 Paco Coursey 和他的设计师小伙伴合力开发的,一周内取得 3k Star。 其特点是无款式的,只提供根底的性能框架,可组合的组件 API,便于扩大,这样一来,你能够基于它二次开发,编写成任何你想要的样子。 官网也做的比拟用心,编写了四种款式作为例子,有 Raycast、Linear、Vercel、Framer 发现 Vue 生态内短少一款好用的 CMDK 类库,于是决定本人造一个(chaoxi)。 如果你还不晓得什么是 CMDK,这里简略介绍下。 CMDK 是一种用户体验CMDK 是 CMD + K 的缩写,CMD 代表 Mac 零碎中的键位 ⌘ ,对应 Command。CMD + K 是组合键,须要同时按下或者先后按下。 其实 CMDK 这种用户体验咱们或多或少都接触过,Mac 自带的 聚焦搜寻 就是这样的一个工具 ⌘ + Space 即可唤起它进行搜寻,或者作为开发者查阅一些文档的时候,都会带有搜寻的性能,有时候会发现都是嵌入的 algolia search, 再或者在应用 VSCode 的时候关上的命令面板(⇧ + ⌘ + P) ...

September 29, 2022 · 2 min · jiezi

关于vue.js:Vue-组件间的通信方式

前言在 Vue 组件库开发过程中,Vue 组件之间的通信始终是一个重要的话题,尽管官网推出的 Vuex 状态治理计划能够很好的解决组件之间的通信问题,然而在组件库外部应用 Vuex 往往会比拟重,本文将零碎的列举出几种不应用 Vuex,比拟实用的组件间的通信形式,供大家参考。 组件之间通信的场景在进入咱们明天的主题之前,咱们先来总结下 Vue 组件之间通信的几种场景,个别能够分为如下几种场景: 父子组件之间的通信兄弟组件之间的通信隔代组件之间的通信 父子组件之间的通信父子组件之间的通信应该是 Vue 组件通信中最简略也最常见的一种了,概括为两个局部:父组件通过 prop 向子组件传递数据,子组件通过自定义事件向父组件传递数据。 父组件通过 prop 向子组件传递数据Vue 组件的数据流向都遵循单向数据流的准则,所有的 prop 都使得其父子 prop 之间造成了一个单向上行绑定:父级 prop 的更新会向下流动到子组件中,然而反过来则不行。这样会避免从子组件意外变更父级组件的状态,从而导致你的利用的数据流向难以了解。 额定的,每次父级组件产生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件外部扭转 prop。如果你这样做了,Vue 会在浏览器的控制台中收回正告。 父组件 ComponentA: <template> <div> <component-b title="welcome"></component-b> </div></template><script>import ComponentB from './ComponentB'export default { name: 'ComponentA', components: { ComponentB }}</script>复制代码子组件 ComponentB: <template> <div> <div>{{title}}</div> </div></template><script>export default { name: 'ComponentB', props: { title: { type: String, } }} </script>复制代码子组件通过自定义事件向父组件传递数据在子组件中能够通过 $emit 向父组件产生一个事件,在父组件中通过 v-on/@ 进行监听。 ...

September 28, 2022 · 5 min · jiezi

关于vue.js:js异步编程的三种模式

写在后面 javascript语言的执行环境是"单线程"(single thread),就是指一次只能实现一件工作。如果有多个工作,就必须排队,等后面一个工作实现,再执行前面一个工作,以此类推。 这种模式的益处是实现起来比较简单,执行环境绝对单纯;害处是只有有一个工作耗时很长,前面的工作都必须排队等着,会迁延整个程序的执行。单线程function f1() { console.log('1')}function f2() { console.log('2')}f1()f2() 很容易能够看出,上述代码会顺次输入1,2。因为代码是从上到下,顺次执行,执行完f1(),才会执行f2()。然而如果f1()中的代码执行的是读取文件或者ajax操作呢,文件的读取都须要肯定工夫,难道咱们须要齐全等到文件齐全读完再进行写操作么?为了解决这个问题,接下来咱们来探索一下js中 同步和异步 的概念。 同步和异步同步 指在 主线程上排队执行的工作,只有前一个工作执行结束,能力继续执行下一个工作。也就是调用一旦开始,必须这个调用 返回后果(划重点——)能力持续往后执行。程序的执行程序和工作排列程序是统一的。异步 异步工作是指不进入主线程,而进入 工作队列的工作,只有工作队列告诉主线程,某个异步工作能够执行了,该工作才会进入主线程。每一个工作有一个或多个 回调函数。前一个工作完结后,不是执行后一个工作,而是执行回调函数,后一个工作则是不等前一个工作完结就执行。程序的执行程序和工作的排列程序是不统一的,异步的。咱们罕用的setTimeout和setInterval函数,Ajax都是异步操作。那么如何实现异步编程呢,笔者介绍几种办法 Web前端视频解说:进入学习回调函数(Callback)回调函数,这是异步编程最根本的办法。 const fs = require('fs')fs.readFile('./pakage.json',(err,info) => { fs.writeFile('./p.json',info,(err) => { if(!err) { setTimeout(() => { console.log('ok') },2000) } })})上述代码通过回调函数的嵌套,从文件系统中读取一个./pakage.json文件并写入./p.json,读取胜利两秒后输入'ok'。用回调来实现异步,没有什么问题。 然而试想,如果再多几个异步函数,代码整体的维护性,可读性都变的极差,如果出了bug,修复过程也变的极为艰难,这个便是所谓的 回调函数天堂。 Promise对象Promise对象用于示意一个异步操作的最终状态(实现或失败),以及其返回的值。MDN对Promise定义如上,Promise本意为承诺,咱们能够了解为程序承诺过一段时间后会给你一个后果。Promise是一个对象,能够保留三个状态 每一时刻必须有一个状态。 胜利 Fulfilled失败 Rejected解决中 Pending默认 pending 如果调用 resolve fulfilled默认 pending 如果调用 reject rejeced const fs = require('fs')const promise1 = new Promise((resolve,reject) => { fs.readFile('./package.json',(err,info) => { resolve(info) })})const promise2 = (info) => { new Promise((resolve,reject) => { fs.writeFile('./p.json', info,(err) => { if(!err) { resolve(); }else{ reject(); } }) })}const promise3 = (time) => { return new Promise((resolve,reject) => { setTimeout(() => { resolve() },time) })}//then链式调用//读文件胜利 将后果作为参数传入promise2promise1.then((info) => { return promise2(info)}).then(() => { // 等着后面的promise console.log('读写实现') return promise3(2000)}).then( ()=> { console.log('ok')}) 这么一看,并没有什么区别,还比下面的异步回调简单,得先新建Promise再定义其回调。但其实,Promise的真正弱小之处在于它的多重链式调用,能够防止层层嵌套回调。 咱们先应用new来构建一个promise。Promise承受一个函数作为参数,该函数的两个参数别离是resolve和reject。 resolve:胜利时调用,并将后果,作为参数传递进来; reject:失败时调用,并将谬误,作为参数抛出。 ...

September 28, 2022 · 1 min · jiezi

关于vue.js:javascript-高级编程-之-Array-用法总结

援用类型是一种数据结构,用于将数据和性能分割起来。 创建对象的形式: 1.new操作符 var array=new Array();2.字面量表示法创立 var array=[];Array检测数组:检测数组是根本类型还是援用类型转换方法:将数组转换成字符串或数组对象栈办法:后进先出的操作数组的办法队列办法:先进先出的操作数组的办法操作方法:数组的拼接、截取、插入、删除、替换地位办法:查找数组项、返回索引值迭代办法:对每个数组项进行操作的办法放大办法:操作数组的每一项,构建最终的返回值1 检测数组检测数组的办法;instanceof操作符的问题是当开发环境引入多个框架存在多个全局环境的时候,会呈现不同的Array构造函数,进而呈现不同的后果。 Array.isArray()这个办法很好的解决了这个问题。arrName instanceof Array var array=[1,2,3];console.log(array instanceof Array) //trueArray.isArray(arrName) console.log(Array.isArray(array)) //trueWeb前端视频解说:进入学习2 转换方法toString():返回以逗号分隔拼接而成的字符串valueOf():返回对象toLocaleString():区别很小,如果是数组调用这个办法,那么数组的每一项都会调用这个办法alert(value)==alert(value.toString()) var array=[1,2,3];var arrayToString=array.toString();var arrayValueOf=array.valueOf();var arrayToLocalString=array.toLocaleString(); console.log(arrayToString);// 1,2,3console.log(arrayValueOf);//[1, 2, 3]console.log(arrayToLocalString);//1,2,33 栈办法 (LIFO:last in first out)ES数组相似于数据结构的办法 栈是一种限度插入和删除项的数据结构push():接管任意数量的参数增加至数组尾部,返回数组长度值pop():从数组开端移除最初一项,缩小数组的length值,返回该数组被删除的最初一项4 队列办法 (FIFO:first in first out)联合push()和shift()办法能够实现像队列一样应用数组 应用unshift()和pop()能够从相同的方向模仿队列shift()移除并返回该数组的第一项;unshift()从数组前端增加任意个参数,并返回新数组的长度5 操作方法concat()复制原数组连贯新数组造成新正本; var arr1=['q','w','e'];var arr2=['h','u','o'];document.write(arr1.concat(arr2)); //q,w,e,h,u,oslice() 有一个参数时,复制参数为起始地位到开端的正本;有两个参数时,复制两个数字两头局部的数组项;如果参数是正数,复制用数组的长度加上负数值失去的两个参数之间的数组项; var arr3=['h','e','l','l','o'];console.log(arr3.slice(1));//e,l,l,oconsole.log(arr3.slice(-4));//e,l,l,oarr3.slice(-4)===arr3.slice(1);//truesplice() 三个参数:别离对应起始地位,删除项的个数,替换项;通过对这三个参数的正当使用能够实现删除、插入、替换等操作。 //从第一项开始删除两项var splice_arr1=['h','e','l','l','o']; console.log(splice_arr1.splice(1,2))//返回的是被删除的项组成的数组["e", "l"] //从第二项后插入三项old var splice_arr2=['h','e','l','l','o']; var removed=splice_arr2.splice(2,0,"K","K"); console.log(splice_arr2);//["h", "e", "K", "K", "l", "l", "o"] console.log(removed)//返回的是一个空数组 //替换 var removed=splice_arr3.splice(2,2,"P","P");console.log(splice_arr3);//["h", "e", "P", "P", "o"]console.log(removed)//返回的是被替换的值["l", "l"]6 地位办法返回索引值indexOf() 从前往后找lastIndexOf() 从后往前找 ...

September 28, 2022 · 1 min · jiezi

关于vue.js:面试官Vue2和3有什么区别

响应式原理api的扭转Vue2响应式原理采纳的是defineProperty,而vue3选用的是proxy。这两者前者是批改对象属性的权限标签,后者是代理整个对象。性能上proxy会更加优良。diff算法,渲染算法的扭转Vue3优化diff算法。不再像vue2那样比对所有dom,而采纳了block tree的做法。此外从新渲染的算法里也做了改良,利用了闭包来进行缓存。这使得vue3的速度比vue2快了6倍。建设数据 data这里就是Vue2与Vue3 最大的区别 — Vue2应用选项类型API(Options API)比照Vue3合成型API(Composition API) 旧的选项型API在代码里宰割了不同的属性(properties):data,computed属性,methods,等等。新的合成型API能让咱们用办法(function)来宰割,相比于旧的API应用属性来分组,这样代码会更加简便和整洁。区别于vue2组件写法,咱们在定义一个vue2的组件的时候,更多是通过一个对象来表白组件,像这样: 而在vue3中,咱们会通过办法的组合调用来实现组件的定义,像这样: 、 vue2 export default { props: { title: String }, data () { return { username: '', password: '' } }}相干vue实战视频解说:进入学习Vue3 应用以下三步来建设反馈性数据: 从vue引入reactive应用reactive()办法来声名咱们的数据为反馈性数据应用setup()办法来返回咱们的反馈性数据,从而咱们的template能够获取这些反馈性数据import { reactive } from 'vue'export default { props: { title: String }, setup () { const state = reactive({ username: '', password: '' }) return { state } }}创立一个 template组件来说,大多代码在Vue2和Vue3都十分类似。Vue3反对碎片(Fragments),就是说在组件能够领有多个根节点。这种新个性能够缩小很多组件之间的div包裹元素。在开发vue的时候,咱们会发现每一个组件都会有个div元素包裹着。就会呈现很多层多余的div元素。碎片(Fragments)解决了这个问题。在Vue3的惟一真正的不同在于数据获取。Vue3中的反馈数据(Reactive Data)是蕴含在一个反馈状态(Reactive State)变量中。— 所以咱们须要拜访这个反馈状态来获取数据值。vue2<template> <div class='form-element'> <h2> {{ title }} </h2> <input type='text' v-model='username' placeholder='Username' /> <input type='password' v-model='password' placeholder='Password' /> <button @click='login'> Submit </button> <p> Values: {{ username + ' ' + password }} </p> </div></template>vue3 ...

September 28, 2022 · 3 min · jiezi

关于vue.js:Vue3知识点之数据侦测

Vue 的外围之一就是响应式零碎,通过侦测数据的变动,来驱动更新视图。 实现可响应对象的形式通过可响应对象,实现对数据的侦测,从而告知外界数据变动。实现可响应对象的形式: getter 和 setterdefinePropertyProxy对于前两个 API 的应用形式不多赘述,繁多的拜访器 getter/setter 性能绝对简略,而作为 Vue2.x 实现可响应对象的 API - defineProperty ,API 自身存在较多问题。 Vue2.x 中,实现数据的可响应,须要对 Object 和 Array 两种类型采纳不同的解决形式。 Object 类型通过 Object.defineProperty 将属性转换成 getter/setter ,这个过程须要递归侦测所有的对象 key,来实现深度的侦测。 为了感知 Array 的变动,对 Array 原型上几个扭转数组本身的内容的办法做了拦挡,尽管实现了对数组的可响应,但同样存在一些问题,或者说不够不便的状况。同时,defineProperty 通过递归实现 getter/setter 也存在肯定的性能问题。 更好的实现形式是通过 ES6 提供的 Proxy API。 Proxy API 的一些细节Proxy API 具备更加弱小的性能,相比旧的 defineProperty API ,Proxy 能够代理数组,并且 API 提供了多个 traps ,能够实现诸多性能。 这里次要说两个trap: get 、 set , 以及其中的一些比拟容易被疏忽的细节。 相干vue实战视频解说:进入学习细节一:trap 默认行为let data = { foo: 'foo' }let p = new Proxy(data, { get(target, key, receiver) { return target[key] }, set(target, key, value, receiver) { console.log('set value') target[key] = value // ? }})p.foo = 123// set value通过 proxy 返回的对象 p 代理了对原始数据的操作,当对 p 设置时,便能够侦测到变动。然而这么写实际上是有问题,当代理的对象数据是数组时,会报错。 ...

September 28, 2022 · 6 min · jiezi

关于vue.js:vue用法

1.模板语法-动静参数<a v-bind:[attributeName]="url"> ... </a>这里的 attributeName 会被作为一个 JavaScript 表达式进行动静求值,求得的值将会作为最终的参数来应用。例如,如果你的 Vue 实例有一个 data property attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href。 <a v-on:[eventName]="doSomething"> ... </a>在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus。 束缚1.动静参数预期会求出一个字符串,异常情况下值为 null。这个非凡的 null 值能够被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个正告。2.因为某些字符,如空格和引号,放在 HTML attribute 名里是有效的,变通的方法是应用没有空格或引号的表达式,或用计算属性代替这种简单表达式。 <!-- 这会触发一个编译正告 --><a v-bind:['foo' + bar]="value"> ... </a>3.在 DOM 中应用模板时 (间接在一个 HTML 文件里撰写模板),还须要防止应用大写字符来命名键名,因为浏览器会把 attribute 名全副强制转为小写: <!--在 DOM 中应用模板时这段代码会被转换为 `v-bind:[someattr]`。除非在实例中有一个名为“someattr”的 property,否则代码不会工作。--><a v-bind:[someAttr]="value"> ... </a>

September 28, 2022 · 1 min · jiezi

关于vue.js:Vue源码解读之InitState

后面咱们讲到了_init函数的执行流程,简略回顾下: 初始化生命周期-initLifecycle初始化事件-initEvents初始化渲染函数-initRender调用钩子函数-beforeCreate初始化依赖注入-initInjections初始化状态信息-initState初始化依赖提供-initProvide调用钩子函数-created一共通过下面8步,init函数执行实现,开始mount渲染。初始化状态信息本章咱们次要解说initState函数的处理过程,咱们先看下init的主函数 function initState(vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) { initProps(vm, opts.props) } if (opts.methods) { initMethods(vm, opts.methods) } if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) { initComputed(vm, opts.computed) } if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) }}看下面代码,先申明了一个_watchers的空数组;而后顺次判断传递进来的options是否蕴含系列参数;顺次执行initProps、initMethods、initData、initComputed、initWatch。 Vue3外围源码视频解说:进入学习initPropsinitProps函数次要是解决传进来的props对象,然而这个props对象是在上一篇文章中讲到的normalizeProps函数解决后的对象,不是传递进来的原对象。来看下initProps的代码: function initProps(vm: Component, propsOptions: Object) { const propsData = vm.$options.propsData || {} const props = vm._props = {} const keys = vm.$options._propKeys = [] const isRoot = !vm.$parent if (!isRoot) { toggleObserving(false) } for (const key in propsOptions) { keys.push(key) const value = validateProp(key, propsOptions, propsData, vm) defineReactive(props, key, value) if (!(key in vm)) { proxy(vm, `_props`, key) } } toggleObserving(true)}下面代码解读: ...

September 28, 2022 · 4 min · jiezi

关于vue.js:Vue3源码解读之patch

例子代码本篇将要解说dom diff,那么咱们联合上面的例子来进行解说,这个例子是在上一篇文章的根底上,加了一个数据变更,也就是list的值产生了扭转。html中减少了一个按钮change,通过点击change按钮来调用change函数,来扭转list的值。例子位于源代码/packages/vue/examples/classic/目录下,上面是例子的代码: const app = Vue.createApp({ data() { return { list: ['a', 'b', 'c', 'd'] } }, methods: { change() { this.list = ['a', 'd', 'e', 'b'] } }});app.mount('#demo')<!DOCTYPE html><html><head> <meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no,target-densitydpi=medium-dpi,viewport-fit=cover" /> <title>Vue3.js hello example</title> <script src="../../dist/vue.global.js"></script></head><body><div id="demo"> <ul> <li v-for="item in list" :key="item"> {{item}} </li> </ul> <button @click="change">change</button></div><script src="./hello.js"></script></body></html>Vue3源码视频解说:进入学习源码解读对于Vue3中数据产生变更,最终影响到页面发生变化的过程,咱们本篇文章只对componentEffect以及当前的代码进行解说,对于数据变更后,是如何执行到componentEffect函数,以及为何会执行componentEffect,前面的文章再进行解说。 componentEffect来看下componentEffect更新局部的代码: // @file packages/runtime-core/src/renderer.ts function componentEffect() { if (!instance.isMounted) { // first render } else { let {next, bu, u, parent, vnode} = instance let originNext = next let vnodeHook: VNodeHook | null | undefined if (next) { updateComponentPreRender(instance, next, optimized) } else { next = vnode } next.el = vnode.el // beforeUpdate hook if (bu) { invokeArrayFns(bu) } // onVnodeBeforeUpdate if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) { invokeVNodeHook(vnodeHook, parent, next, vnode) } const nextTree = renderComponentRoot(instance) const prevTree = instance.subTree instance.subTree = nextTree if (instance.refs !== EMPTY_OBJ) { instance.refs = {} } patch( prevTree, nextTree, hostParentNode(prevTree.el!)!, getNextHostNode(prevTree), instance, parentSuspense, isSVG ) next.el = nextTree.el if (originNext === null) { updateHOCHostEl(instance, nextTree.el) } // updated hook if (u) { queuePostRenderEffect(u, parentSuspense) } // onVnodeUpdated if ((vnodeHook = next.props && next.props.onVnodeUpdated)) { queuePostRenderEffect(() => { invokeVNodeHook(vnodeHook!, parent, next!, vnode) }, parentSuspense) } } }当数据发生变化的时候,最终会走到下面的else的逻辑局部。 ...

September 28, 2022 · 6 min · jiezi

关于vue.js:Vuejs设计与实现电子版源码学习

1、KeepAlive 组件的实现原理KeepAlive 的实质是缓存治理,再加上非凡的挂载/卸载逻辑。 首先,KeepAlive 组件的实现须要渲染器层面的反对。这是因为被 KeepAlive 的组件在卸载时,咱们不能真的将其卸载,否则就无奈维持组件的以后状态了。正确的做法是,将被 KeepAlive 的组件从原容器搬运到另外一个暗藏的容器中,实现“假卸载”。当被搬运到隐藏容器中的组件须要再次被“挂载”时,咱们也不能执行真正的挂载逻辑,而应该把该组件从暗藏容器中再搬运到原容器。这个过程对应到组件的生命周期,其实就是 activated 和 deactivated。 const KeepAlive = { // KeepAlive 组件独有的属性,用作标识 __isKeepAlive: true, setup(props, { slots }) { // 创立一个缓存对象 // key: vnode.type // value: vnode const cache = new Map() // 以后 KeepAlive 组件的实例 const instance = currentInstance // 对于 KeepAlive 组件来说,它的实例上存在非凡的 keepAliveCtx 对象,该对象由渲染器注入 // 该对象会裸露渲染器的一些外部办法,其中 move 函数用来将一段 DOM 挪动到另一个容器中 const { move, createElement } = instance.keepAliveCtx // 创立暗藏容器 const storageContainer = createElement('div') // KeepAlive 组件的实例上会被增加两个外部函数,别离是 _deActivate和 _activate // 这两个函数会在渲染器中被调用 instance._deActivate = (vnode) => { move(vnode, storageContainer) } instance._activate = (vnode, container, anchor) => { move(vnode, container, anchor) } return () => { // KeepAlive 的默认插槽就是要被 KeepAlive 的组件 let rawVNode = slots.default() // 如果不是组件,间接渲染即可,因为非组件的虚构节点无奈被 KeepAlive if (typeof rawVNode.type !== 'object') { return rawVNode } // 在挂载时先获取缓存的组件 vnode const cachedVNode = cache.get(rawVNode.type) if (cachedVNode) { // 如果有缓存的内容,则阐明不应该执行挂载,而应该执行激活 // 继承组件实例 rawVNode.component = cachedVNode.component // 在 vnode 上增加 keptAlive 属性,标记为 true,防止渲染器从新挂载它 rawVNode.keptAlive = true } else { // 如果没有缓存,则将其增加到缓存中,这样下次激活组件时就不会执行新的挂载动作了 cache.set(rawVNode.type, rawVNode) } // 在组件 vnode 上增加 shouldKeepAlive 属性,并标记为 true,防止渲染器真的将组件卸载 rawVNode.shouldKeepAlive = true // 将 KeepAlive 组件的实例也增加到 vnode 上,以便在渲染器中拜访 rawVNode.keepAliveInstance = instance // 渲染组件 vnode return rawVNode } } }2、异步组件的实现原理function defineAsyncComponent(options) { // options 能够是配置项,也能够是加载器 if (typeof options === 'function') { // 如果 options 是加载器,则将其格式化为配置项模式 options = { loader: options, }; } const { loader } = options; let InnerComp = null; return { name: 'AsyncComponentWrapper', setup() { const loaded = ref(false); // 代表是否超时,默认为 false,即没有超时 const timeout = ref(false); loader().then((c) => { InnerComp = c; loaded.value = true; }); let timer = null; if (options.timeout) { // 如果指定了超时时长,则开启一个定时器计时 timer = setTimeout(() => { // 超时后将 timeout 设置为 true timeout.value = true; }, options.timeout); } // 包装组件被卸载时革除定时器 onUmounted(() => clearTimeout(timer)); // 占位内容 const placeholder = { type: Text, children: '' }; return () => { if (loaded.value) { // 如果组件异步加载胜利,则渲染被加载的组件 return { type: InnerComp }; } else if (timeout.value) { // 如果加载超时,并且用户指定了 Error 组件,则渲染该组件 return options.errorComponent ? { type: options.errorComponent } : placeholder; } return placeholder; }; }, };} ...

September 27, 2022 · 2 min · jiezi

关于vue.js:vue实现子组件渲染父级组件的插槽vue3

在编写vue通用组件时常常会遇到子组件的插槽需从父组件传递过去,并还能够给插槽绑定数据,如ant-design-vue中的下拉菜单多选项时能够通过插槽自定义tag的内容 先展现下成果:组件默认成果: 应用了tag插槽的成果: 1、思路外围思路是:将父组件中的插槽以参数的模式传递给子孙组件,而不是应用插槽传递,如<template #xxx><slot name="xxx"></slot></template> 这里有两种形式能够将父组件的插槽以参数模式传递给子孙组件: provide+inject模式子组件定义props,父组件将slots传递给子组件这两种形式各有千秋,看本人喜爱了 2、编码:应用 provide+inject 形式实现父组件 <template> <div class="input-tags"> <input type="text" class="input-tags-inputer"> <div class="input-tags-list"> <MyTag v-for="tag in tags" :key="tag.value" :tag-data="tag"></MyTag> </div> </div></template><script>import { provide} from 'vue';import MyTag from './MyTag.vue';export default { name: 'InputTags', props: { tags: { type: Array, default () { return []; } } }, components: { MyTag }, setup (props, ctx) { provide('inputTagsCtx', ctx); return {}; }};</script><style lang="scss">.input-tags{ //display: inline-block; position: relative;}.input-tags-inputer{ display: block; width: 100%; height: calc(1.5em + 0.75rem + 2px); padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: #495057; background-color: #fff; background-clip: padding-box; border: 1px solid #ced4da; border-radius: 0.25rem; transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; &:focus{ color: #495057; background-color: #fff; border-color: #80bdff; outline: 0; box-shadow: 0 0 0 0.2rem rgb(0 123 255 / 25%); }}.input-tags-list{ position: absolute; top: 1px; left: 1px; bottom: 1px; right: 1px; padding: 0.25rem 0 0 0.5rem; .my-tag{ margin-right: 0.5rem; }}</style>子组件 ...

September 26, 2022 · 3 min · jiezi

关于vue.js:手写vuerouter核心原理

最近也在察看vue3新个性,抽空玩一玩嵌套路由的vue-router,间接上代码 我的项目目录构造 代码展现app.vue<template> <div id="app"> <div> <router-link to="/">Index</router-link> | <router-link to="/person">Person</router-link> | <router-link to="/person/info">PersonInfo</router-link> </div> <!-- 一级路由 --> <router-view /> </div></template><style>#app{ display: flex; flex-direction: column; align-items: center;}</style>Index.vue<template> <div class="index"> <h1>this is index page</h1> </div></template>Person.vue<template> <div class="person"> <h1>this is person page</h1> <!-- 二级路由 --> <router-view /> </div></template>PersonInfo.vue<template> <div class="personInfo"> <h2>this is personInfo page</h2> </div></template>vue全家桶视频解说:进入学习js文件main.jsimport Vue from 'vue'import App from './App.vue'import router from './router'new Vue({ router, render: h => h(App)}).$mount('#app')babel.config.js须要增加babel依赖反对新语法,如可选链npm install --save-dev @babel/core @babel/clinpm install --save-dev @babel/plugin-proposal-optional-chainingmodule.exports = { presets: [ '@babel/preset-env' ], plugins: ['@babel/plugin-proposal-optional-chaining']}router目录下文件index.jsimport Vue from "vue";import VueRouter from "./vue-router";import Index from "../views/Index.vue";import Person from "../views/Person.vue";import PersonInfo from "../views/PersonInfo.vue";Vue.use(VueRouter);const routes = [ { path: "/", name: "Index", component: Index }, { path: "/person", name: "Person", component: Person, children:[ { path: "/person/info", name: "PersonInfo", component: PersonInfo } ] }];const router = new VueRouter({ routes});export default router;vue-router.js这里先借助Vue的工具Vue.util.defineReactive实现数据响应式,后续再手撕这个库import routerLink from "./router-link";import routerView from "./router-view";let Vue;class VueRouter { constructor(options) { this.$options = options this.current = window.location.hash.slice(1) || "/" // 设置响应式数组数据 Vue.util.defineReactive(this, "routerArray", []) // 监听hash值变动 window.addEventListener("hashchange", this.hashChange.bind(this)) this.getRouterArray() } hashChange() { this.current = window.location.hash.slice(1) || "/" this.routerArray = [] this.getRouterArray() } getRouterArray(routes) { routes = routes || this.$options.routes for (const route of routes) { if (this.current === '/' && route.path === '/') { this.routerArray.push(route) return } if (this.current.indexOf(route.path) !== -1 && route.path !== '/') { this.routerArray.push(route) if (route.children) { // 递归子路由 this.getRouterArray(route.children) } return } } }}VueRouter.install = function(_Vue) { Vue = _Vue // Vue全局混入,等new Vue中的router实例创立之后挂载到Vue上 Vue.mixin({ beforeCreate() { if (this.$options.router) { Vue.prototype.$router = this.$options.router } }, }); // 注册router-link和router-view全局组件 Vue.component("router-link", routerLink) Vue.component("router-view", routerView)}export default VueRouterrouter-link.jsexport default { props: { to: { type: String, required: true } }, render(h) { return h( "a", { attrs: { href: "#" + this.to, }, }, this.$slots.default ); }};router-view.jsexport default { render(h) { // 设置嵌套路由标识 this.$vnode.data.rv = true // 嵌套路由设置深度 let depth = 0 let parent = this.$parent while (parent) { // 下级还有嵌套路由标识rv为true的,深度加一 if (parent.$vnode?.data?.rv) { depth++ } parent = parent.$parent } // 简略解决 const route = this.$router.routerArray[depth] return h(route?.component); }};效果图 好了,明天就玩到这里了,下次再玩别的哈 ...

September 26, 2022 · 2 min · jiezi

关于vue.js:手把手写一个Vuerouter无惧面试官的vueRoute题目

一、外围原理1.什么是前端路由?在 Web 前端单页利用 SPA(Single Page Application)中,路由形容的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变动引起 UI 更新(无需刷新页面)。 2.如何实现前端路由?要实现前端路由,须要解决两个外围: 如何扭转 URL 却不引起页面刷新?如何检测 URL 变动了?上面别离应用 hash 和 history 两种实现形式答复下面的两个外围问题。 hash 实现hash 是 URL 中 hash (#) 及前面的那局部,罕用作锚点在页面内进行导航,扭转 URL 中的 hash 局部不会引起页面刷新 通过 hashchange 事件监听 URL 的变动,扭转 URL 的形式只有这几种: 通过浏览器后退后退扭转 URL通过<a>标签扭转 URL通过window.location扭转URLhistory 实现history 提供了 pushState 和 replaceState 两个办法,这两个办法扭转 URL 的 path 局部不会引起页面刷新 history 提供相似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同: 通过浏览器后退后退扭转 URL 时会触发 popstate 事件通过pushState/replaceState或<a>标签扭转 URL 不会触发 popstate 事件。好在咱们能够拦挡 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变动通过js 调用history的back,go,forward办法课触发该事件所以监听 URL 变动能够实现,只是没有 hashchange 那么不便。 ...

September 26, 2022 · 6 min · jiezi

关于vue.js:Vue3-setup语法糖Composition-API全方位解读

起初 Vue3.0 裸露变量必须 return 进去,template 中能力应用;Vue3.2 中 只须要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需 return,template 可间接应用。本文章以Vue2的角度学习Vue3的语法,让你疾速了解Vue3的Composition Api本文章第十四节为状态库 Pinia 的装置、应用解说一、文件构造Vue2中,<template> 标签中只能有一个根元素,在Vue3中没有此限度 <template> // ...</template><script setup> // ...</script><style lang="scss" scoped> // 反对CSS变量注入v-bind(color)</style>二、data<script setup> import { reactive, ref, toRefs } from 'vue' // ref申明响应式数据,用于申明根本数据类型 const name = ref('Jerry') // 批改 name.value = 'Tom' // reactive申明响应式数据,用于申明援用数据类型 const state = reactive({ name: 'Jerry', sex: '男' }) // 批改 state.name = 'Tom' // 应用toRefs解构 const {name, sex} = toRefs(state) // template可间接应用{{name}}、{{sex}}</script>vue实战视频解说:进入学习三、method<template> // 调用办法 <button @click='changeName'>按钮</button> </template><script setup> import { reactive } from 'vue' const state = reactive({ name: 'Jery' }) // 申明method办法 const changeName = () => { state.name = 'Tom' } </script>四、computed<script setup> import { computed, ref } from 'vue' const count = ref(1) // 通过computed取得doubleCount const doubleCount = computed(() => { return count.value * 2 }) // 获取 console.log(doubleCount.value)</script>五、watch<script setup> import { watch, reactive } from 'vue' const state = reactive({ count: 1 }) // 申明办法 const changeCount = () => { state.count = state.count * 2 } // 监听count watch( () => state.count, (newVal, oldVal) => { console.log(state.count) console.log(`watch监听变动前的数据:${oldVal}`) console.log(`watch监听变动后的数据:${newVal}`) }, { immediate: true, // 立刻执行 deep: true // 深度监听 } )</script>六、props父传子子组件<template> <span>{{props.name}}</span> // 可省略【props.】 <span>{{name}}</span></template><script setup> // import { defineProps } from 'vue' // defineProps在<script setup>中主动可用,无需导入 // 需在.eslintrc.js文件中【globals】下配置【defineProps: true】 // 申明props const props = defineProps({ name: { type: String, default: '' } }) </script>父组件引入子组件,组件会主动注册 ...

September 26, 2022 · 7 min · jiezi

关于vue.js:vue源码中的nextTick是怎样实现的

一、Vue.nextTick 外部逻辑在执行 initGlobalAPI(Vue) 初始化 Vue 全局 API 中,这么定义 Vue.nextTick。 function initGlobalAPI(Vue) { //... Vue.nextTick = nextTick;}能够看出是间接把 nextTick 函数赋值给 Vue.nextTick,就能够了,非常简单。 二、vm.$nextTick 外部逻辑Vue.prototype.$nextTick = function (fn) { return nextTick(fn, this)};能够看出是 vm.$nextTick 外部也是调用 nextTick 函数。 三、前置常识nextTick 函数的作用能够了解为异步执行传入的函数,这里先介绍一下什么是异步执行,从 JS 运行机制说起。 vue源码相干视频解说:进入学习1、JS 运行机制JS 的执行是单线程的,所谓的单线程就是事件工作要排队执行,前一个工作完结,才会执行后一个工作,这就是同步工作,为了防止前一个工作执行了很长时间还没完结,那下一个工作就不能执行的状况,引入了异步工作的概念。JS 运行机制简略来说能够按以下几个步骤。 所有同步工作都在主线程上执行,造成一个执行栈(execution context stack)。主线程之外,还存在一个工作队列(task queue)。只有异步工作有了运行后果,会把其回调函数作为一个工作增加到工作队列中。一旦执行栈中的所有同步工作执行结束,就会读取工作队列,看看外面有那些工作,将其增加到执行栈,开始执行。主线程一直反复下面的第三步。也就是常说的事件循环(Event Loop)。2、异步工作的类型nextTick 函数异步执行传入的函数,是一个异步工作。异步工作分为两种类型。 主线程的执行过程就是一个 tick,而所有的异步工作都是通过工作队列来一一执行。工作队列中寄存的是一个个的工作(task)。标准中规定 task 分为两大类,别离是宏工作(macro task)和微工作 (micro task),并且每个 macro task 完结后,都要清空所有的 micro task。 用一段代码形象介绍 task的执行程序。 for (macroTask of macroTaskQueue) { handleMacroTask(); for (microTask of microTaskQueue) { handleMicroTask(microTask); }}在浏览器环境中,常见的创立 macro task 的办法有 ...

September 26, 2022 · 5 min · jiezi

关于vue.js:如何正确学习vue30源码

为什么要学源码技术是第一生产力学习 API 的设计目标、思路、取舍学习优良的代码格调学习组织代码的形式学习实现办法的技巧学习 ES67 新 API、TS 高级用法不给本人设限,不要让你四周人的技术下限成为你的下限面试加分项装逼利器学习源码副作用画虎不成反类犬(强行下马 vue3,本人焦头烂额、我的项目难以保护、共事苦不堪言)为了用而用,而不是就地取材喜爱炫技写一下看似搞大上,理论没有可读性,影响团队合作的奇技淫巧vue3 设计动机与目标更好的逻辑复用与代码组织 vue2 option api 的代码格调将同一逻辑点的代码扩散在各处,会导致读者关注点拆散,也不利于代码的逻辑复用;而 vue3 composition api 将同一业务逻辑的代码聚合在一起命名为 useXXX 函数,再通过 setup 将不同的逻辑组装起来并返回给组件 data,显著更不便逻辑复用。vue2 mixin 用于逻辑复用的时候容易导致命名抵触和数据起源不清晰;而 vue3 provide/inject 配合 composition api 能够很不便的找到数据起源并通过解构重命名,显著更不便逻辑复用。更好的类型推导 在 methods 中 this 指向组件实例而不是 method 自身,不利于类型推导。例如 this.router、this.store,每个新的插件都会须要向 Vue 追加类型定义。vue源码相干视频解说:进入学习更新前后比照优化打包更小(全局 API tree-shaking)渲染、更新更快,内存占用缩小应用 proxy 取代 Object.definePropertyv-model 代替以前的 v-model 和.sync生命周期变更 例如 destroyed beforeDestroy 改为 unmounted beforeUnmount自定义指令 API 与生命周期保持一致Diff 算法的晋升(动态标记、动态晋升)新个性Template 反对多个根标签composition API 实现逻辑模块化和复用Teleport 传送门组件 代码块挂载到任意地位Suspense 悬停组件 异步加载组件应用(试验属性)应用 @vue/runtime-core 的 createRenderer 自定义渲染器(跨平台利器)应用 ts 编写源码,更好的类型推导、更好的适配 ts更多变动v3.cn.vuejs.org/guide/migra…疑难解答问题一:compostion api 基本没有解决任何问题,只是追赶新玩意的货色尤雨溪: 不批准这个观点。Vue 最开始很小,然而当初被广泛应用到不同级别复杂度的业务畛域,有些能够基于 option API 很轻松解决,然而有些不能够。例如上面的场景: ...

September 26, 2022 · 1 min · jiezi

关于vue.js:数据大屏开发流程梳理和3d地图绘制

碎碎念最近一段时间刚好公司须要开发数据大屏,用Echarts做完发现走了不少的弯路,趁明天有空把开发流程从新梳理了下。老规矩先放图: ps:具体实际请间接拉到最上面 事先沟通筹备数据大屏我集体认为最大的难点不在于Echarts的配置项多,而是来自于产品和UI天马行空的想象力。因而在开发前墙裂倡议拉上产品和UI开个小会,防止前期的返工。 产品方面和产品确认好数据的起源和束缚,依据展现空间的大小和服务器的性能来预判正当的数据量。以柱状图为例,X轴的数据点如果太过于密集,就会放大以至于重叠,须要让产品给出数据点的最小/大数量来判断需不要开启缩放拖拽等(这部分也会影响ui设计的版面大小和交互)。Y轴的数据的起源是通过业务数据推算,还是手动管制,如果出现异常数据(极小值或者极大值)了之后需不需要过滤。还有始终比拟极其的状况就是Y轴数据达到了千万以上级别(只是举个栗子),可能会呈现数据被遮挡的状况,需不需要改用迷信计数法(影响UI)图表的抉择,严格来说这部分的确不属于开发的活,不过思考到产品同学或者甲方爸爸可能对前端现有的图表不太熟悉,能够参考以下这张图:UI如果能够的话,在出图之前能够让UI同学先去看下Echarts或者antV的示例我的项目,对立好格调和要应用的技术栈,加上产品给的数据束缚范畴,在对应出图。这应该是最完满的状况了。最坏的状况就是究极交融怪了,这个时候如果有些奇奇怪怪的图表比方3d环图(能够做,但的确费时间)这种,尽量拉上产品一起沟通,通通有效后最初杀招就是这个需要做不了。总结最常呈现的问题就是,产品只思考了最完满的状况,忽视了一些异样或者极其状况。同时UI同学也只是对着原型设计出图,这个时候开发最好还是能提前辨认到这些危险项,而后把问题抛出来(甩锅什么的,肯定要赶早)。UI思考的好看和信息展现水平抵触也是比拟常见的。比方图例如果是竖向排序,只有两个数据源的状况下没有问题,然而数据源一多样式就崩了,这种状况下如果UI一开始给的版面没有思考到这种状况,那也只能把图例信息去掉。设计阶段大屏设计稿的尺寸市面上常见的屏幕比例是16:9,分辨率1920 * 1080。当然最好还是问下产品和UI次要是针对哪方面的人群。以他们的意见为准。有几个小细节可能须要提前留神和产品Ui沟通(其余的见下面) 整个数据大屏允不容许呈现滚动条,即是不是铺满整个屏幕。(按理来说是不该呈现的)如果是铺满整个屏幕。还须要确认下是不是默认全屏(F11)。这也关系到UI的出图尺寸,如果不是默认全屏又须要铺满屏幕,就还要扣掉浏览器下面的局部。高度就是可视区域的高度。一些笔记本的默认缩放比是125的,如果业务场景是放在电视或者其余显示器设施上固定展现的影响不大,当时调整就行。自适应分辨率当用户的屏幕分辨率不合乎咱们的设计稿时,就须要动静适配。目前市面的解决方案有应用rem,transform缩放,vh/vw等。 笔者这边应用的是rem。 次要思路是动静批改根节点的fontSize值,而后通过px转rem的函数实现自适应, // 在数据大屏的页面,初始化调用这个办法created() { // 依据屏幕大小设置款式。 this.setFontSize() const that = this //窗口变动后从新设置 window.onresize = function () { that.setFontSize() }}/** * @description: 设置基准字体值 * @return {*} */ setFontSize() { let designWidth = 1920 //设计稿的宽度 let designHeight = 1080 //设计稿的高度 // 这边是以宽度为优先级进行设置的 const fontSize = (document.documentElement.clientWidth / designWidth) * 12 document.querySelector('html').style.fontSize = fontSize + 'px' }less写法 @width: 1920;//设计稿的宽度@height: 1080;//设计稿的高度@design_font_size: 12;.px2rem(@key, @px) { @{key}:(@px/@design_font_size) * 1rem;}//-----------------应用分割线------------- .icon { .px2rem(height,24); .px2rem(width,24); }sass写法 ...

September 25, 2022 · 2 min · jiezi

关于vue.js:vue封装第三方组件

需要场景咱们可能在工作中常常会遇到这样的场景,须要基于某个UI组件库(本文以Vuetify为例,其余的也同理)封装一个可复用的组件,不仅要实现自定义性能,还心愿能沿用第三方组件自身提供的属性,事件等。举几个具体的栗子: 对立给text-field增加一个clearable的动作想更改text-field默认label款式,由点击悬浮变为固定地位给image组件增加loading的成果给table组件对立增加一些诸如挪动列,刷新,csv下载的动作技术关键点一、继承属性v-bind="$attrs" 和 inheritAttrs: false$attrs蕴含了父组件里除了props属性的 attribute 绑定,所以在子组件中应用$attrs就能够获取到父组件传递的属性。然而默认状况下这些属性会被认作是一般的HTML attribute利用到子组件的根元素上,当初咱们像创立一个wrapper来做封装时很可能会呈现不合乎预期的状况,此时咱们就须要设置inheritAttrs: false来打消这种默认行为。 vue2.x和vue3.x中的区别$attrs在vue2和vue3中的体现有所不同,在vue2里,$attrs不蕴含style和class,而在vue3中蕴含 二、继承事件v-on="$listeners"$listeners里蕴含了父组件作用域中v-on 事件监听器。咱们在子组件中能够通过v-on="$listeners"来获取父组件传入的事件 vue2.x和vue3.x中的区别$listeners在vue3中被移除了,能够间接通过$attrs来代替 三、继承插槽$scopedSlots 和 $slotsvuetify中提供了非常灵活的自定义插槽,所以对于插槽的继承也是十分重要滴 <template v-for="(_, scopedSlotName) in $scopedSlots" #[scopedSlotName]="slotData"> <slot :name="scopedSlotName" v-bind="slotData" /></template><template v-for="(_, slotName) in $slots" #[slotName]> <slot :name="slotName" /></template>参考文章https://dev.to/onurelibol/cre...https://dev.to/kouts/create-w...

September 23, 2022 · 1 min · jiezi

关于vue.js:使用-vueclasssetup-编写-class-风格来的组合式API支持Vue2和Vue3

前言我司基于vue-class-component开发的我的项目有上百个,其中部署的 SSR 服务也靠近100个,如此宏大体量的我的项目一开始的时候还空想着看看是否要降级Vue3,后果调研一番下来,才发现vue-class-component对Vue3的反对,最初一个版本公布都过来两年了,迟迟还没有公布正式版本。目前基本上处于无人保护的状态,而且降级存在着大量的破坏性更新,对于将来是否还要持续应用Vue3当初还是持保留意见,然而不障碍咱们先把组件库做成Vue2和Vue3通用,于是就有了本文。 在过来的三年里,vue-class-component最大的问题是就是无奈正确的校验组件的传参,事件类型,这给我带来了微小的暗影,在通过一番调研后,惊喜的发现应用defineComponent定义的组件,在Vue2.7和3.x都能够正确的辨认类型,所以先打算外部的组件库先做到同时反对Vue2和Vue3,如果前面还要持续采纳Vue3就变得容易得多。 于是,回到了结尾,调研了一番vue-class-component在Vue3的反对,目前最新的版本是8.0.0-rc.1,后果大喜过望,目前基本上处于无人保护的状态,社区内又没有一个能满足我需要的,同时反对Vue2和Vue3的。 诞生想法鉴于vue-class-component组件目前无奈做到正确的组件类型测验,当我惊喜的发现组合式API写进去的代码能够被正确的辨认类型时,诞生了一个应用 class 格调来编写组合式API的想法,于是破费一个月的实际,踩遍了所有的坑,终于诞生了vue-class-setup,一个应用 class 格调来编写代码的库,它gzip压缩后,1kb大小。 疾速开始npm install vue-class-setup<script lang="ts">import { defineComponent } from 'vue';import { Setup, Context } from 'vue-class-setup';// Setup 和 Context 必须一起工作@Setupclass App extends Context { private _value = 0; public get text() { return String(this._value); } public set text(text: string) { this._value = Number(text); } public onClick() { this._value++; }}export default defineComponent({ // 注入类实例的逻辑 ...App.inject(),});</script><template> <div> <p>{{ text }}</p> <button @click="onClick()"></button> </div></template>尝试多很多种计划,最终采纳了下面的模式为最佳实际,它无奈做到export default间接导出一个类,必须应用defineComponent 来包装一层,因为它只是一个组合类(API),并非是一个组件。 ...

September 23, 2022 · 3 min · jiezi

关于vue.js:vue中transition实现div缓慢显示隐藏动画

vue中显示或者暗藏DOM时,提供多种不同的形式来实现动画成果,能够配合应用css3动画,也能够应用第三方动画库,如animate.css。这里咱们应用vue提供的 transition 组件。 <!DOCTYPE HTML><html> <head> <title></title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"> <script type="text/javascript" src="js/vue.min.js"></script> <style> .popBackground{ width: 300px; height: 200px; background: rgba(0,0,0,0.4); color: #01FEFE; } .fade-enter-active, .fade-leave-active { transition: opacity .8s; } .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; } </style> </head> <body> <div id="box"> <div id="demo"> <button v-on:click="show = !show"> Toggle </button> <transition name="fade"> <div v-show="show" class="popBackground"> hello </div> </transition> </div> </div> <script> var box = new Vue({ el: '#box', data () { return { show: true } } }) </script> </body></html>

September 21, 2022 · 1 min · jiezi

关于vue.js:2022十大开源vue3-admin后台系统框架

说到应用vue做一个治理后盾零碎,大部分人都会应用饿了么的框架vue-element-admin,因为很不便,很快就可能搭建进去一个丑陋的零碎,然而有的时候,会不满足本人的业务需要的款式,这个时候,我就总结了7个宝藏级Vue治理后盾框架 ,给喜爱应用vue技术栈的小伙伴门。 1:vue-admin-better 个性 40+高质量单页 RBAC 模型 + JWT 权限管制 10 万+ 我的项目理论利用 良好的类型定义 开源版本反对收费商用 跨平台 PC、手机端、平板️ 后端路由动静渲染 地址 github 仓库地址 码云仓库地址 vue2.x + element-ui(收费商用,反对 PC、平板、手机)⚡️ vue3.x + element-plus(alpha 版本,收费商用,反对 PC、平板、手机)⚡️ vue3.x + ant-design-vue(beta 版本,收费商用,反对 PC、平板、手机)⚡️ vue3.x + vite + arco admin pro 演示地址(vue2.x 付费版本,反对 PC、平板、手机) admin plus 演示地址(vue3.x 付费版本,反对 PC、平板、手机)2:ant-design-vue-pro举荐指数:star:11.2k官网:https://www.antdv.com/docs/vu...GitHub 地址:https://github.com/vueCompone... 3: iview-admin举荐指数:star:15kGitHub 地址:https://github.com/iview/ivie...演示地址:https://admin.iviewui.com iView admin 是基于 iView 的 Vue 2.0 控制面板。搭配应用 iView UI 组件库造成的一套后盾集成解决方案 。 4:d2-admin举荐指数:star:9kGitHub地址:https://github.com/d2-project... D2Admin 是一个齐全 开源收费 的企业中后盾产品前端集成计划,基于 vue.js 和 ElementUI 的管理系统前端解决方案 ,小于 60kb 的本地首屏 js 加载,曾经做好大部分项目前期筹备工作 ...

September 21, 2022 · 1 min · jiezi

关于vue.js:vueadminbetter文档

简体中文 | English <div align="center"><img width="200" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/logo/vab.png"/><h1> vue-admin-better</h1> <p>春已至,万物始,愿所有美妙纷沓而来!</p></div> 个性 40+高质量单页 RBAC 模型 + JWT 权限管制 10 万+ 我的项目理论利用 良好的类型定义 开源版本反对收费商用 跨平台 PC、手机端、平板️ 后端路由动静渲染 地址 vue2.x + element-ui(收费商用,反对 PC、平板、手机)⚡️ vue3.x + element-plus(alpha 版本,收费商用,反对 PC、平板、手机)⚡️ vue3.x + ant-design-vue(beta 版本,收费商用,反对 PC、平板、手机)⚡️ vue3.x + vite + arco admin pro 演示地址(vue2.x 付费版本,反对 PC、平板、手机) admin plus 演示地址(vue3.x 付费版本,反对 PC、平板、手机) pro 及 plus 购买地址 authorization Vue Shop Vite 商城(付费版本)行将公布,敬请期待! github 仓库地址 码云仓库地址<!-- ## 备份地址(反对 https 网站自动更新) ...

September 21, 2022 · 2 min · jiezi

关于vue.js:爆肝撸了个羊了个羊通关助手

最近,羊了个羊甚是火爆,以至于在上市几天后作者就在广东全款买了两套房,作为一个非游戏迷我对这一状况真是不解,不就一个开心消消乐游戏,为啥会如此火爆呢?或者这就是小游戏的魅力吧,一如之前的QQ农场、FlyBird。据说很多人在第二关就栽了,作为程序员的我也想蹭着这波热量来给大家撸一个“羊了个羊”通关助手。 前言本我的项目是应用 Vue2 + Node.js实现羊了个羊疾速通关,仅为学习应用,请勿应用本程序歹意对游戏服务器继续造成压力,所有后果自负!!! 用到的抓包工具: Fiddler/HTTPDebugger/Charles【PC】HttpCarry【Android】Stream【iphone】反对的个性: 反对在线填写抓包参数反对随机生成工夫和自定义工夫最大反对10次通关,能够依据本人需要更改启动运行# 拷贝代码git clone https://github.com/hu-snail/vue-sheep.git# 进入我的项目cd vue-sheep# 装置依赖yarn # or npm i# 运行yarn serve提醒:因为我的项目是应用额Vue2,所以在运行的时候可能会报错。常见的报错和解决方案如下:报错1 Invalid options in vue.config.js: "css.modules" is not allowed解决办法:将css节点的modules:false删除。 报错2 ERROR in [eslint] ESLint is not a constructorERROR in Error: Child compilation failed:[eslint] ESLint is not a constructor这是因为开启了eslint查看,因为版本不统一可能会报这个谬误。 解决办法:敞开eslint,在vue.config.js文件中中增加:lintOnSave: false。 运行截图 我的项目源码: https://github.com/hu-snail/vue-sheep

September 21, 2022 · 1 min · jiezi

关于vue.js:vue中Liveplayer的使用

liveplayer和easyplayer应用办法很类似,属性也基本相同,上面开始liveplayer应用办法:1.npm装置liveplayer、copy-webpack-plugin npm install @liveqing/liveplayernpm install copy-webpack-plugin@4.0.1 --save-dev也可在package.json文件中间接增加版本号,而后执行yarn install 或 npm install: "dependencies": { "@liveqing/liveplayer": "2.3.3",},"devDependencies": { "copy-webpack-plugin": "^4.0.1",}2.在vue.config.js文件中配置 const CopyWebpackPlugin = require('copy-webpack-plugin')...new CopyWebpackPlugin([ { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml' }, { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/' }, { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf' }, { from: 'node_modules/@liveqing/liveplayer/dist/element/liveplayer-element.min.js', to: 'js/' },])3.在public/index.html中引入js动态资源留神:文件地位要与下面配置的to...雷同,下面配置是的 to: 'js/',所以底下地址是./js <!--引入liveplayer依赖--><script src="./js/liveplayer-lib.min.js"></script><script src="./js/liveplayer-element.min.js"></script>或<script src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script><script src="<%= BASE_URL %>js/liveplayer-element.min.js"></script>4.在vue中应用 <live-player :video-url="item.videoUrl" :poster="item.poster" :live="true" :stretch="true" :controls="item.carouselShow ? false : true" :hide-big-play-button="true" alt=""></live-player>...import LivePlayer from '@liveqing/liveplayer'...components: { LivePlayer }

September 20, 2022 · 1 min · jiezi

关于vue.js:vue中easyplayer使用

1.npm装置easyplayer、copy-webpack-plugin npm install @easydarwin/easyplayer --savenpm install copy-webpack-plugin@4.0.1 --save-dev也可在package.json文件中间接增加版本号,而后执行yarn install 或 npm install: "dependencies": { "@easydarwin/easyplayer": "^5.0.3",},"devDependencies": { "copy-webpack-plugin": "^4.0.1",}2.在vue.config.js文件中配置 const CopyWebpackPlugin = require('copy-webpack-plugin')...new CopyWebpackPlugin([ { from: 'node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer.wasm' }, { from: 'node_modules/@easydarwin/easyplayer/dist/component/crossdomain.xml' }, { from: 'node_modules/@easydarwin/easyplayer/dist/component/EasyPlayer-lib.min.js', to: 'js/' },])3.在public/index.html中引入js动态资源留神:文件地位要与下面配置的to...雷同,下面配置是的 to: 'js/',所以底下地址是./js <!--引入EasyPlayer依赖--><script src="./js/EasyPlayer-lib.min.js"></script>或<script src="<%= BASE_URL %>js/EasyPlayer-lib.min.js"></script>4.在vue中应用 <easy-player :video-url="item.videoUrl" :live="true"></easy-player>...import EasyPlayer from '@easydarwin/easyplayer'...components: { EasyPlayer }

September 20, 2022 · 1 min · jiezi

关于vue.js:后盾人vue3

一、创立我的项目yarn create vite base --template vue

September 20, 2022 · 1 min · jiezi

关于vue.js:若依ruoyiui主窗口appmain全屏显示方法

以若依首页为例:1.store/modules/settings.js增加一个navbar_tags: ....const state = {.... navbar_tags: true // navbar/tags-view显示与暗藏}....const actions = {.... // navbar/tags-view显示与暗藏 setNavbar_tags({ commit }, navbar_tags) { state.navbar_tags = navbar_tags }}....2.views/index.vue增加一个全屏按钮: <div class="signOut" @click="fullscreen" v-if="!winfull.full"> <img class="ico" src="@/assets/images/index/ico_big.png"/> <div class="text">放大</div></div><div class="signOut" @click="fullscreen" v-else> <img class="ico" src="@/assets/images/index/ico_signOut.png"/> <div class="text">退出</div></div>export default { data() { return { // 窗口放大 winfull: { full: false } }; }, methods: { // app-main层全屏显示开关 fullscreen() { this.winfull.full = !this.winfull.full; if (this.winfull.full) { this.$store.dispatch('app/toggleSideBarHide', true); this.$store.dispatch('settings/setNavbar_tags', false); } else { this.$store.dispatch('app/toggleSideBarHide', false); this.$store.dispatch('settings/setNavbar_tags', true); } } }};3.layout/index.vue ...

September 20, 2022 · 1 min · jiezi

关于vue.js:让mixin为项目开发助力及递归优化新尝试

背景咱们通常会遇到这么一个场景:有几个基本功能一样的组件,然而他们之间又存在着足够的差别。这时候你就来到了一个岔路口:我是把他们“循序渐进”地写成不同的组件呢?还是保留为一个“公共组件”,而后通过props传参进行不同性能之间的辨别呢? 当初还有一个场景:在一些组件(甚至是我的项目中全副和某个性能无关的组件)中,有某个性能是雷同的。而且都须要利用这个性能进行后续操作。你又须要抉择了,然而这次有一个前提:必定是要“复用”的 —— 公共组件?还是 mixin ? 这里其实笔者集体认为并将其分为“css- UI复用”和“性能复用”两种形式。这里先按下不提。本文默认讲的是后者。咱们当初来剖析下:在第一个场景中,其实两种解决方案都不够完满:如果拆分成多个组件,你就不得不冒着一旦性能变动就要在所有相干文件中更新代码的危险,这违反了 DRY 准则;反之,太多的 props 传值会让代码变得凌乱不堪,后续难以保护、团队了解艰难,效率升高。那有没有更好的办法?再来看第二个场景,其实咱们很分明地晓得:这时候咱们须要的不是一个能够传值的组件,而是一个相似于插件一样的 js 代码(这么说可能了解吧)! 应用Mixin吧Vue 中的 Mixin 对编写函数式格调的代码很有用,因为函数式编程就是通过缩小挪动的局部让代码更好了解。Mixin 容许你封装一块在利用的其余组件中都能够应用的函数。如果应用姿态切当,他们不会扭转函数作用域内部的任何货色。因而哪怕执行屡次,只有是同样的输出你总是能失去一样的值。 如何应用mixin其实有两种写法 —— Object 和 Function。它们都能够在单个组件或者全局中援用。但对于 function 模式的mixin,笔者更举荐将其作为组件级别应用(而非全局的)。 先看第一种写法:假如有一对不同的组件,它们的作用是通过切换状态(Boolean)来展现或者暗藏模态框或提示框。这些提示框和模态框除了性能类似以外,没有其余共同点:它们看起来不一样,用法不一样,然而逻辑一样。这时咱们能够将它们的公共逻辑局部封装为一个js文件: // mixins目录下的toggle.js文件export const toggle = { data() { return { isShowing: false } }, methods: { toggleShow() { this.isShowing = !this.isShowing; } }}个别咱们抉择新建一个专门的mixin目录。在外面创立一个文件含有.js扩展名,为了应用Mixin咱们须要输入一个对象。(es6 Modules) 而后应用mixins:[] 的形式引入mixin文件,(引入后)对象中的属性可间接应用(就像结尾说的“插件”一样): import { toggle } from './mixins/toggle';//...const Modal = { template: '#modal', mixins: [toggle], //...};const Tooltip = { template: '#tooltip', mixins: [toggle], //...};第二种写法:这种模式其实就特地实用于结尾说的第二种状况。因为 mixin 外部一个组件该有的它都能够具备。而且下面也说了:当mixin被引入后它外部的货色能够被间接应用 —— 其实就是被merge到援用它的组件中了!(相当于对父组件的扩大) ...

September 19, 2022 · 4 min · jiezi

关于vue.js:Vue-Parsing-error-invalidfirstcharacteroftagname-报错定位与解决

前一段时间总在写React我的项目,tsx中写dom用三元运算符用的多。这次写Vue我的项目,把写三元运算符的习惯带到了vue的模板语法中,同时条件表达式中用了小于号"<",后果编译失常,网页展现失常,eslint报错"invalid-first-character-of-tag-name"。 举个例子 解决办法(回到Vue的世界,别在React里打转):1、filter (不举荐,Vue3废除了filter) 2、methods外面定义一个function用于格式化 (举荐) 3、应用本义符 (骚操作领域,不举荐) "<" 变为 "&lt;"这种状况下eslint校验通过,编译可通过,页面也失常显示,然而装了vscode插件Volar (或者Vetur) 的状况下应用本义符的中央会飘红。 4、应用v-text (不举荐,不能复用) 完结 同步更新到本人的语雀https://www.yuque.com/diracke...

September 16, 2022 · 1 min · jiezi

关于vue.js:vue组件间通信

前言本章咱们将介绍组件间是如何实现数据通信的。包含父组件向子组件、子组件向父组件、兄弟组件、非关系组件之间的数据通信。组件通信是组件式开发中十分重要的一部分,也是组件式开发中的难点。组件介绍组件是 vue 最弱小的性能之一,而组件实例的作用域是互相独立的,这就意味着不同组件之间的数据无奈互相援用。咱们须要应用特定的形式来实现组件间的数据通信,接下来让咱们一个个介绍这几种类别的组件通信是如何实现的。 一、父传子1. 父组件通过 props 传递数据给子组件父组件通过 props 属性向子组件传递数据。子组件利用组件实例的 props 属性定义组件须要接管的参数,在应用组件时通过 attribute的形式传入参数。 // 在子组件内定义组件接管一个参数 name{ props: ['name']}// 父组件应用组件时传递参数 name<child :name="name"></child>接下来咱们看一个具体示例: <!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>Document</title></head><body> <div id="app"> <parent></parent> </div></body><script src="https://unpkg.com/vue/dist/vue.js"></script><script type="text/javascript"> Vue.component('parent', { template: '<child :name="name"></child>', data() { return { name: '句号' } } }) Vue.component('child', { template: '<div>{{name}}</div>', props: ['name'] }) var vm = new Vue({ el: '#app', data() { return {} } })</script></html>==代码解释==JS 代码第 14-18 行:定义了组件 child,并用 props 接管一个参数 name。JS 代码第 4-12 行:定义了组件 parent,在组件中应用 <child></child> 援用组件,并用 attribute 的形式将 name 传递给组件 child。 ...

September 16, 2022 · 4 min · jiezi

关于vue.js:使用数组方法indexOf判断两个数组里面是否有相同项如果不包含则添加itemchecked-true

最近在开发一个性能,点击增加商品按钮,展现商品列表弹层,点击复选框选中,弹层会敞开,并且再次点击商品按钮,之前被选中的复选框会不可点击,目标就是避免反复增加同一件商品,用到了数组办法indexOf(),用来判断我商品列表外面的商品有没有被选中过,如果选中了,则设置item.checked = true。我把点击按钮展现弹层的步骤给省略掉了。上面是我的代码: <!DOCTYPE html><html><head> <meta charset="utf-8"> <title>应用element-ui中的el-radio(单选框)组件实现选中和勾销选中性能</title> <!--引入 element-ui 的款式,--> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script> <!-- 引入element 的组件库--> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <style> * { margin: 0; padding: 0; } </style></head><body> <div id="app"> <div style="text-align: center; font-size: 20px">模仿一个弹层</div> <el-table :data="list" border fit highlight-current-row header-cell-class-name="chatail-table-header-cell"> <el-table-column label="商品名称" align="center" min-width="80"> <template slot-scope="{ row }"> <span>{{ row.name }}</span> </template> </el-table-column> <el-table-column label="操作" align="center" min-width="60" fixed="right" class-name="small-padding fixed-width"> <template slot-scope="{ row }"> <el-checkbox v-model="row.checked" :disabled="row.checked" @change="checkedFun(row)">{{ row.checked ? "已选" : "抉择" }}</el-checkbox> </template> </el-table-column> </el-table> </div> <script> new Vue({ el: '#app', data() { return { list: [ { id: 1, name: '测试商品1' }, { id: 2, name: '测试商品2' }, { id: 3, name: '测试商品3' }, { id: 4, name: '测试商品4' }, { id: 5, name: '测试商品5' }, ] } }, mounted() { this.getProductList([1, 3, 5]); }, methods: { getProductList(checkedIdArr) { this.list.forEach((item) => { item.checked = false; if (checkedIdArr && checkedIdArr.length > 0) { // 传入的数据和列表里的数据比照,如果没有,则让复选框选中 if (checkedIdArr.indexOf(item.id) > -1) { item.checked = true; } } }); // 或者这样赋值也能够 // this.list = this.list.map((item) => { // item.checked = false; // if (checkedIdArr && checkedIdArr.length > 0) { // if (checkedIdArr.indexOf(item.id) > -1) { // item.checked = true; // } // } // return item; // }); }, checkedFun(row) { let index = this.list.map(item => item.id).indexOf(row.id) // 这样写也能够 // findIndex 用来获取数组中第一个满足条件的元素下标 // let index = this.list.findIndex((value, index, arr) => { // return value.id == row.id // }) //这样就能够渲染到视图层了 this.$set(this.list, index, { ...row, checked: this.list[index].checked }) console.log(this.list, 'this.list---') }, } }) </script></body></html>

September 15, 2022 · 2 min · jiezi

关于vue.js:Vue中动态拼接innerHTML时添加点击click事件并调用vue方法

代码如下,通过在innerHtml增加onclick='windowAddClickTag(1)'办法 let content = document.createElement('div') content.innerHTML = `<div onclick='windowAddClickTag(1)'> <p style='background:#0849B4;border: 2px solid #FFFFFF; padding: 0 13px; width: 120px;color: #FFFFFF;text-align: center;line-height: 48px;'>自定义标签</p> <img src="${icon}" style='width: 40px; height: 40px; margin: 0 40px;'"/> </div>`并且在mounted增加该办法 mounted() { window.windowAddClickTag = data => { this.handleClickTag(data) } },就能够调用了methods: { handleClickTag(data){ console.log(data) }, }

September 15, 2022 · 1 min · jiezi

关于vue.js:ReportDesign100行代码搞定一个功能完整的报表页你不心动吗

应用文档地址:http://43.143.54.159/ github地址:https://github.com/hunan-liy/... ReportDesign是一款基于ElementUI进行扩大的Vue开源组件库,以数据驱动视图,通过简略的配置生成一个残缺的页面,次要蕴含Form 表单、Table 表格、Report 报表和DialogSelect 弹窗选择器组件,次要解决开发过程中以下痛点: 格调不对立:尤其是在治理后盾的我的项目中,对于大部分页面,格调其实根本是对立的,然而开理论开发过程中,就算应用了ElementUI,因为合作开发,可能每个人写进去的页面都有差别,然而对于治理后盾页面如果叫UI出设计稿又太节约老本了,所以咱们通过组件的形式对立页面格调,保障每个开发人员写出的页面都保持一致的格调。 性能脱漏:细节性能脱漏其实在开发过程中很容易呈现,如Form 表单的校验、占位文字,Report页面的分页入参谬误、条件查问字段漏传、查问后分页没有切换到第一页等,这些细节问题尽管在提交测试过程中最初会被发现并解决,然而为什么咱们不从根本上防止这种谬误的呈现呢?通过应用组件的形式,如Report 报表组件,开发人员只用关注页面须要哪些查问条件,须要展现哪些字段就行,接口申请全副封装在组件外部去做,缩小呈现低级性能脱漏问题的几率。 开发效率:失常开发一个须要查问条件、须要分页、须要排序功能的报表页面须要多久?半天?2个小时?1个小时?不,只有你复制粘贴的够快,10分钟就够了!因为每个页面须要的配置根本始终,咱们定义好开发模板,对照的接口文档将须要展现的字段的label和prop以及接口地址复制粘贴替换掉,10分钟开发一个简略报表页齐全足够。 生成这样的一个页面只须要100行代码,轻松搞定! 该组件库是联合作者工作教训,重新整理后独立开发实现,各位同行如有发现其中不足之处,欢送领导。

September 14, 2022 · 1 min · jiezi

关于vue.js:OpenDataV低代码平台新增组件流程

OpenDataV打算采纳子库的形式增加子组件,即每一个组件都当做一个子库,子库有本人的依赖,而我的项目自身的依赖只针对框架,因而每一个组件咱们都当做一个子库来开发。上面我带着大家一步步具体的开发一个数字展现组件。 创立组件目录和文件进入组件库目录下所有的可拖拽组件都寄存在src/resource/components目录下 cd src/resource/components依据组件名称创立目录默认组件目录是以组件的名称命名,当然也能够依据本人的要求命名,组件能够放在components目录下,也能够放在其子目录的目录下。 mkdir DigitalText创立组件所需的固定文件每个组件必须的文件有vue文件xxx.vue、配置文件config.ts、导出文件index.ts,每个文件有本人的用途,vue文件不用说了是组件渲染的主体,导出文件名称固定为index.ts,次要是导出组件的信息给内部援用,配置文件次要是在编辑页面右侧展现的配置项,这个咱们前面再详述。所以这里咱们须要创立三个文件:DigitalText.vue、config.ts、index.ts 以上咱们就创立好了组件所需的文件,上面就须要做组件的初始化了 初始化组件文件因为咱们的组件都是以子库的形式引入,所以须要进行包的初始化,执行以下命令 cd src/resource/components/Text/DigitalTextnpm init这里应用npm初始化包会让咱们抉择填写局部数据。 上面咱们先初始化一下组件文件DigitalText.vue,先初始化最简略的组件数据 <template> <div>数字展现</div></template><script lang="ts" setup></script><style lang="less" scoped></style>而后咱们要初始化组件的配置文件config.ts import { ComponentGroup, FormType } from '@/enum'import type { PropsType } from '@/types/component'import { BaseComponent } from '@/resource/models'export const componentName = 'Digital'class DigitalComponent extends BaseComponent { constructor(id?: string, name?: string, icon?: string) { super({ component: componentName, group: ComponentGroup.TEXT, name: name ? name : '数字文本', id, width: 100, height: 30, icon }) }}export default DigitalComponent这里要阐明的点:componentName是组件在我的项目中的注册名称,因而必须保障惟一,group是给组件分组,这里的分组次要是展现在组件拖拽页面,其类型的ComponentGroup是固定好的,能够本人减少,展现地位如下: ...

September 12, 2022 · 3 min · jiezi

关于vue.js:vue中父子组件异步传递数据

应用v-if指令来动态创建子组件子组件中: <template> <div>{{value}}</div></template><script>export default { props: { value: { type: String } }}</script>在父组件中: <template> <div> <div v-if="viewReady"> <child v-bind:value=""/> </div> <div></template><script>export default { data() { return { viewReady: false } }, created() { this.getData() }, methods: { getData() { return new Promise(r=> { // request r() }).then(res => { this.viewReady = true }) } }}</script>

September 11, 2022 · 1 min · jiezi

关于vue.js:vue模板中定义使用临时变量

有时候在模板中循环中,须要去计算一个值,并且屡次应用。因为是一个循环,所以不太好应用computed,就不得不须要屡次计算 <li v-for="(item,index) in list" :key="index"> <div>{{method(item.key)}}</div> <div>{{method(item.key)}}</div></li>这种状况能够在模板中长期定义一个长期变量 <li v-for="(item,index) in list" :key="index" :data-var="value = method(item.key)"> <div>{{value}}</div> <div>{{value}}</div></li>并不是只能应用data-var,这个是随便写的,只是一个承载这个长期变量的载体,你也能够这样写。 <li v-for="(item,index) in list" :key="index"> <div style="display:none;">{{value = method(item.key)}}</div> <div>{{value}}</div> <div>{{value}}</div></li>

September 9, 2022 · 1 min · jiezi

关于vue.js:Vue-elementui-全局样式覆写不成功问题排查解决

前景提要:Vue2 + element-ui我的项目,对element-ui的样式表进行覆写,实现自定义款式。 失常状况下,咱们在Vue2的我的项目中应用element-ui,须要1、引入element-ui2、Vue.use(Element)3、引入样式表。当初要对element-ui的样式表进行覆写,作用范畴为全局。覆写款式不胜利。 状况排查:对element-ui进行全局款式覆写时,发现新增的样式表B胜利增加,但被另外的样式表笼罩。控制台Elements点击element-ui组件,能看到两份样式表A,样式表B的款式,问题是样式表B的款式大段的被划掉,上面又有新的样式表C,样式表C笼罩了自定义的样式表B。 这个状况下,全局搜寻"theme-chalk",大概率能在babel.config.js中找到一个plugins(见到上面这段代码) [ "component", { libraryName: "element-ui", styleLibraryName: "theme-chalk" }]把它正文掉,再看控制台Elements,能够发现样式表C没了,解决。 未完待续。 同步更新到本人的语雀https://www.yuque.com/diracke...

September 9, 2022 · 1 min · jiezi

关于vue.js:一文搞懂pinia状态管理

Vue3曾经推出很长时间了,它周边的生态也是越来越欠缺了。之前咱们应用Vue2的时候,Vuex能够说是必备的,它作为一个状态管理工具,给咱们带来了极大的不便。Vue3推出后,尽管绝对于Vue2很多货色都变了,然而外围的货色还是没有变的,比如说状态治理、路由等等。再Vue3种,尤大神举荐咱们应用pinia来实现状态治理,他也说pinia就是Vuex的新版本。那么pinia到底是何方神圣,本篇文章带大家一起学透它! 1.pinia是什么? 如果你学过Vue2,那么你肯定应用过Vuex。咱们都晓得Vuex在Vue2中次要充当状态治理的角色,所谓状态治理,简略来说就是一个存储数据的中央,寄存在Vuex中的数据在各个组件中都能拜访到,它是Vue生态中重要的组成部分。 既然Vuex那么重要,那么在Vue3中岂能抛弃! 在Vue3中,能够应用传统的Vuex来实现状态治理,也能够应用最新的pinia来实现状态治理,咱们来看看官网如何解释pinia的。官网解释:Pinia 是 Vue 的存储库,它容许您跨组件/页面共享状态。 从下面官网的解释不难看出,pinia和Vuex的作用是一样的,它也充当的是一个存储数据的作用,存储在pinia的数据容许咱们在各个组件中应用。实际上,pinia就是Vuex的升级版,官网也说过,为了尊重原作者,所以取名pinia,而没有取名Vuex,所以大家能够间接将pinia比作为Vue3的Vuex。 2.为什么要应用pinia? 很多小伙伴心田是抗拒学习新货色的,比方咱们这里所说的pinia,很多小伙伴可能就会抛出一系列的疑难:为什么要学习pinia?pinia有什么长处吗?既然Vue3还能应用Vuex为什么我还要学它?...... 针对下面一系列的问题,我置信很多刚开始学习pinia的小伙伴都会有,包含我本人当初也有这个疑难。当然,这些问题其实都有答案,咱们不可能平白无故的而去学习一样货色吧!必定它有本人的长处的,所以咱们这里先给出pinia的长处,大家心里先有个大略,当你纯熟应用它之后,在会过头来看这些长处,置信你能了解。 长处:Vue2和Vue3都反对,这让咱们同时应用Vue2和Vue3的小伙伴都能很快上手。 pinia中只有state、getter、action,摈弃了Vuex中的Mutation,Vuex中mutation始终都不太受小伙伴们的待见,pinia间接摈弃它了,这无疑缩小了咱们工作量。 pinia中action反对同步和异步,Vuex不反对良好的Typescript反对,毕竟咱们Vue3都举荐应用TS来编写,这个时候应用pinia就十分适合了 无需再创立各个模块嵌套了,Vuex中如果数据过多,咱们通常分模块来进行治理,稍显麻烦,而pinia中每个store都是独立的,相互不影响。 体积十分小,只有1KB左右。 pinia反对插件来扩大本身性能。 反对服务端渲染。 pinia的长处还有十分多,下面列出的次要是它的一些次要长处,更多细节的中央还须要大家在应用的时候缓缓领会。 3.筹备工作 想要学习pinia,最好有Vue3 的根底,明确组合式API是什么。如果你还不会Vue3,倡议先去学习Vue3。 本篇文章解说pinia时,全副基于Vue3来解说,至于Vue2中如何应用pinia,小伙伴们能够自行去pinia官网学习,毕竟Vue2中应用pinia的还是多数。 我的项目搭建:咱们这里搭建一个最新的Vue3 + TS + Vite我的项目。执行命令:npm create vite@latest my-vite-app --template vue-ts运行我的项目:npm installnpm run dev删除app.vue中的其它无用代码,最终页面如下: 4.pinia根底应用 4.1 装置pinia 和vue-router、vuex等一样,咱们想要应用pinia都须要先装置它,装置它也比较简单。 装置命令: yarn add pinia# 或者应用 npmnpm install pinia装置实现后咱们须要将pinia挂载到Vue利用中,也就是咱们须要创立一个根存储传递给应用程序,简略来说就是创立一个存储数据的数据桶,放到应用程序中去。 批改main.js,引入pinia提供的createPinia办法,创立根存储。 代码如下: // main.tsimport { createApp } from "vue";import App from "./App.vue";import { createPinia } from "pinia";const pinia = createPinia();const app = createApp(App);app.use(pinia);app.mount("#app");4.2 创立storestore简略来说就是数据仓库的意思,咱们数据都放在store外面。当然你也能够把它了解为一个公共组件,只不过该公共组件只存放数据,这些数据咱们其它所有的组件都可能拜访且能够批改。 ...

September 7, 2022 · 5 min · jiezi

关于vue.js:vue实例项目

一、装置我的项目创立我的项目 yarn create vite选项的抉择如下: cd router //到我的项目地位yarn //装置依赖包yarn dev 启动我的项目装置路由yarn add vue-router@4路由官网:https://router.vuejs.org/zh/i... 装置path插件yarn add -D path 装置类型申明yarn add -D @types/node yarn add lodashyarn add -D @types/lodash yarn add -D tailwindcss postcss autoprefixernpx tailwindcss init -p //省城配置文件 mock.jsyarn add mockjsyarn add vite-plugin-mock -D

September 6, 2022 · 1 min · jiezi

关于vue.js:项目ms

组件按需加载:装置 unplugin-auto-import 插件,vue.config.js 中进行相干配置,间接应用,不须要import。编译的时候会主动import 组件封装: 图片懒加载: 路由权限:路由守卫 多图上传:(改为购物车组件封装)

September 6, 2022 · 1 min · jiezi

关于vue.js:安装vuecodemirror支持SQL可视化

文章不易,请关注公众号 毛毛虫的小小蜡笔,多多反对,谢谢。 有任何问题都能够留言征询。 npm装置npm install vue-codemirror@4.0.6 --save// oryarn add vue-codemirror@4.0.6 -D这里记得装置的是4.0.6版本。 最新版本会有问题,装置后执行import或require引入,会报404等谬误,具体起因就没去看了。 在main.js引入详情 请查看:毛毛虫的小小蜡笔

September 2, 2022 · 1 min · jiezi

关于vue.js:在-Vue-中为什么不推荐用-index-做-key

尤大在vue 2.x的文档中明确指出:倡议尽可能在应用 v-for 时提供 key attribute,除非遍历输入的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的晋升。 尤大的倡议说白了就是说: 如果index能够做key,那间接底层帮你传进去好了,又何必让你们多此一举呢?乖乖的不要用index做key那么:key 到底有什么用? 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用就地复用策略 。 这句话是什么意思? 让咱们一起去探讨吧 diff算法 简略的说就是新旧虚构dom的比拟,如果有差别就以新的为准,而后再插入的实在的dom中,从新渲染key的作用 一句话: key的作用次要是为了更高效的比照虚构DOM中每个节点是否是雷同节点; 举个简略的例子 三胞胎战成一排,你怎么晓得谁是老大? 如果老大皮了一下子,和老三换了一下地位,你又如何辨别进去? 给他们挂个牌牌,写上老大、老二、老三。 这样就不会认错了。key就是这个作用。 通过 key 治理状态Vue 默认依照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的程序扭转时,Vue 不会随之挪动 DOM 元素的程序,而是就地更新每个元素,确保它们在本来指定的索引地位上渲染。 默认模式是高效的,但只实用于列表渲染输入的后果不依赖子组件状态或者长期 DOM 状态 (例如表单输出值) 的状况。 为了给 Vue 一个提醒,以便它能够跟踪每个节点的标识,从而重用和从新排序现有的元素,你须要为每个元素对应的块提供一个惟一的 key attribute: 在没有 key 的状况下,Vue 将应用一种最小化元素挪动的算法,并尽可能地就地更新/复用雷同类型的元素。如果传了 key,则将依据 key 的变动程序来重新排列元素,并且将始终移除/销毁 key 曾经不存在的元素。 同一个父元素下的子元素必须具备惟一的 key。反复的 key 将会导致渲染异样 效率 & Bug说到这,有些人就是认为是简略的效率问题。 的确:设置key能够让diff更高效,但仅仅是重绘重排吗? 答案是否定的 如果仅仅是效率低下,在操作极少的元素中,也无伤大雅。 然而,应用index带来的问题却要麻烦的多 <div id="app"> <Child v-for="item,i in array" :text="item" @delete="remove(i)"/></div>data() { return { array: [1, 2, 3] };},methods: { remove(i) { this.array.splice(i, 1); }}此时,key的作用就是为了复用。正是因为会复用,所以用index来做key会呈现复用错的问题总结最简略不便的就是:应用数据库中的 id如果返回值中没有id怎么办 ...

September 2, 2022 · 1 min · jiezi

关于vue.js:Vue3nextTick调度分析

nextTick定义:将回调推延到下一个 DOM 更新周期之后执行,在更改了一些数据以期待 DOM 更新后立刻应用它在理论中应用这个办法个别是用于组件更新,你须要获取更新后的数据,所以应用nextTick期待DOM更新 import { createApp, nextTick } from 'vue'const app = createApp({ setup() { const message = ref('Hello!') const changeMessage = async newMessage => { message.value = newMessage // 这里的value是旧值 await nextTick() // nextTick后获取的就是DOM更新后的value console.log('Now DOM is updated') } }})这个api应用时相当简略,而且相当容易了解,然而为了知其意,还是要翻一下源码理解它的执行机制 export function nextTick(this: ComponentPublicInstance | void,fn?: () => void): Promise<void> {const p = currentFlushPromise || resolvedPromisereturn fn ? p.then(this ? fn.bind(this) : fn) : p}下面是vue源码中nextTick的实现,为了搞清楚实现逻辑,就必须搞懂currentFlushPromise这个变量的含意,所以要从工作的调度机制开始剖析任务调度首先这个调度机制的性能在runtime-core的scheduler文件 API ...

August 31, 2022 · 2 min · jiezi

关于vue.js:Vue3模版编译原理

模版编译流程Vue3模版编译就是把template字符串编译成渲染函数 // template<div><p>{{LH_R}}</p></div>// renderimport { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("div", null, [ _createElementVNode("p", null, _toDisplayString(_ctx.LH_R), 1 /* TEXT */) ]))}我会依照编译流程分3步剖析 parse:将模版字符串转换成模版ASTtransform:将模版AST转换为用于形容渲染函数的ASTgenerate:依据AST生成渲染函数 export function baseCompile( template: string | RootNode, options: CompilerOptions = {}): CodegenResult { // ... const ast = isString(template) ? baseParse(template, options) : template const [nodeTransforms, directiveTransforms] = getBaseTransformPreset( prefixIdentifiers ) transform( ast, extend({}, options, { prefixIdentifiers, nodeTransforms: [ ...nodeTransforms, ...(options.nodeTransforms || []) // user transforms ], directiveTransforms: extend( {}, directiveTransforms, options.directiveTransforms || {} // user transforms ) }) ) return generate( ast, extend({}, options, { prefixIdentifiers }) )}parseparse对模版字符串进行遍历,而后循环判断开始标签和完结标签把字符串宰割成一个个token,存在一个token列表,而后扫描token列表并保护一个开始标签栈,每当扫描一个开始标签节点,就将其压入栈顶,栈顶的节点始终作为下一个扫描的节点的父节点。这样,当所有Token扫描实现后,即可构建成一颗树形AST以下是简化版parseChildren源码,是parse的主入口 ...

August 31, 2022 · 3 min · jiezi

关于vue.js:图灵访谈-Vuejs官方团队成员霍春阳跨专业做程序员是什么感受

霍春阳,Vue.js 官网团队成员,专一于 Web 研发畛域, 是 Vue.js 3 的外围贡献者之一,Vue.js 文档生成工具 Vuese 的作者,技术社区沉闷者,曾撰写大量颇受好评的技术博客。大一那年,读机械业余的他开始与同学守业,拿到 10 万元投资。大三那年,他毅然决定肄业,只身返回北京,开启职业程序员的生涯。现在,他已是 Vue.js 官网团队成员,还出版了畅销书《Vue.js设计与实现》。他为何「跨专业」做程序员?为什么决定大学肄业?又是如何退出了 Vue.js 官网团队? 本期图灵访谈带你走近霍春阳,一起来理解他的传奇经验。 01非科班出身的程序员机械业余,因守业喜爱上了编程 我是非科班出身,我的业余其实叫做“资料成型及控制工程”,这个业余和编程其实不沾边。我接触编程是因为⼀个偶尔的机会。大一上学期的时候,咱们学校计算机学院的⼀个同学创建了⼀个计算机社团,叫计算机守业协会。过后他们去咱们班级纳新,我就和他们聊了很多,感觉不错,所以就决定退出这个协会了。其实我过后比拟关注的是“守业”本⾝,而非计算机。那会儿我的想法就是,只有守业即可,至于做不做计算机相关的创业项目并不重要。就这样,我的人生开始和计算机有了交加。退出协会后,我和这个计算机协会的会长成为了十分好的敌人。值得⼀提的是,时至今日,我俩依然是十分好的敌人,其实我在新书《Vue.js设计与实现》的致谢中也提到了他。起初,我俩就率领协会成员开始商议创业项目,敲定了要做⼀款垂直于⼤学⽣的交友软件,叫聘爱⽹,下⾯这张图是咱们当初为了推⼴聘爱⽹⽽做的宣传卡⽚:而我接触编程,就是从咱们一起开发这个网站时开始的。过后去网上看了很多教程,也读了十分多的书,次要都是对于 PHP 语言的。2013年,学校调配的工作室(一间教室)起初第一版网站上线了,感觉十分骄傲,那应该是我第⼀次感触到编程带给我的成就感,兴许就是在这个时候,我开始喜爱上了编程。从大一开始守业,不久后拿到了种子轮 10 万元的投资,并且失去了孵化器专门为咱们提供的办公场合。那会儿年轻气盛,感觉必定就此走上“人生巅峰”了。于是把所有赌注都压到了守业上,从大一下学期开始,我就办理了复学,同时搬到了离学校一小时车程的孵化器“全职”守业。到了大三,也就是 2015 年的暑假,创业项目倒退不景气,团队⾥的其余成员开始缓缓地进来找实习,为毕业工作做筹备了,我也不例外。过后我有两个抉择,⼀是回学校持续读书,然而因为复学的缘故,我可能要到 2018 年能力毕业;⼆是抉择肄业,进来找工作。我思考过后家⾥条件不好,须要我去赚钱补贴家⽤,所以就抉择了肄业,而后决定来北京这个城市工作。之所以抉择来北京,是因为 2015 年的暑假,我接了⼀个外包项⽬,这个我的项目的⽼板过后也在北京守业,起初和⽼板聊得不错,就决定去北京跟着她⼲。于是,我就带着做外包项⽬赚的 3000 块钱坐卧铺从哈尔滨来到了北京,开启了我的北漂之旅(那会儿印象比拟粗浅的是,交不起房租,只能住地下室,还是公开⼆层。很小很小的隔断间,外面布满了蜘蛛⽹)。我⼀开始是写PHP做后端的,做前端齐全是因为我 2015 年来北京工作的第⼀家公司的须要,起初就始终做前端了。对于程序员求职,学历和业余背景并非决定性因素——“是金子总会发光”。我大学肄业次要是出于三点起因:第一是下面提到的守业复学;第二是心愿尽早帮家里减轻负担;第三则是我的业余与计算机无关,我集体认为没有持续读的必要。但我认为学历还是很重要的,要想方法多致力⼀点。要想找到指标工作,那还是得“打铁还需自⾝硬,是金子总会发光”。 02给程序员的学习倡议根底常识奠定了你的可能性 首先,我倡议肯定要学习计算机基础知识,楼盖的高不高,齐全就看根底牢不牢。像我这种非科班出⾝的人,最能领会那种根底不牢固,导致前期成长速度不⾜,提高乏力,短少方向感。我已经花了很长时间来补救这方面的有余,看了十分多的计算机经典书籍。这些常识其实是通用的,不会随着工夫的流逝变得过期。在学习计算机基础知识的时候,会让你感觉短时间内“学无可用”,甚至感觉节约了很多工夫。但请置信我,这么做相对是值得的,它会成为你将来成长的减速剂。其次,英语水平对程序员来说十分重要,这间接影响到你的成长和倒退。我在知乎看到过⼀个十分不错的答复,大略意思是,英语不好会成为你的“枷锁”。就是说你的能力本不止于此,但就是提高不了,到天花板了。我举两个直观的例⼦:我写了⼀个 CSS 解析器,在我的 GitHub 主页能够看到,难度本⾝并不⼤,是依照 CSS 相干的标准编写的,然而这个标准是英文写的,所以如果看不懂英⽂的话,即便这件事儿难度本⾝并不⼤,你也完不成。还有就是 Vue 3 的响应零碎,其实想要实现⼀个欠缺的响应零碎,真的须要看 JavaScript 这门语言的标准,而标准是英文写的,所以即便你再怎么厉害,要是看不懂相应的标准,你还是写不进去。大略这就是所谓的“枷锁”。最初,浏览源码至关重要。看看别⼈解决问题的思路,看看别⼈的代码,而后尝试着⾃⼰通过代码把想法表达出来,在这个过程中缓缓晋升。 03与 Vue.js 结缘 2016 年,我还在第二家公司下班的时候,团队还在用 jQuery+RequireJS。个中痛楚就不⼀⼀赘述了,过后 React 和 Vue 的在技术圈里曾经逐步风行,最初联合咱们本人的业务和团队特点,抉择了 Vue.js。那应该是我第⼀次接触 Vue,过后感觉 Vue 真的很好用。Vue 的学习和上手对老手很敌对。当然要想学好 Vue,还是得有扎实的前端根底。我退出 Vue 官网团队齐全是因为对 Vue 的奉献达到了要求,例如帮忙修 bug 之类的。退出 Vue 官网团队,最大的感触就是⼀种被“认可”的感觉,十分的开⼼,这是真的。 ...

August 29, 2022 · 1 min · jiezi

关于vue.js:在vue已有电脑端页面基础上加入移动端页面

场景:写好了管理系统网页端,忽然需要退出对应的挪动端页面艰深就是:咱们用vue写了一个网页我的项目,而后长期退出一个挪动端我的项目,同一个我的项目,同一套代码 退出挪动端我的项目如果用原生的js去写或者用vue的网页UI框架去写款式都会在事实中遇到困难,网页的UI不适宜挪动端的UI这时候可能会说用rem去转换px不就能够了吗,我想说的是如果构建我的项目的时候你用到了rem,这里过渡能够很天然,然而刚开始你没用rem,这里强去转换有点勉强对于简洁的vue,我找的是vue对应的简洁的挪动端UI - Vant,对用习惯了的element或者iview来说去看vant的应用,会感觉到很难受!vant的地址: https://vant-contrib.gitee.io/vant/v2/#/zh-CN/list举个常见例子,挪动端你遇到要分页的数据,原生要本人监听滑到底部,vant则提供了收费的组件vant-list去给你用,写对传参即可还比方挪动端的工夫组件怎么写,原生吗,van-datetime-picker帮你解决在同我的项目里创立新文件夹写挪动端,package.json里退出vant,能够同时和element等应用,这并不会抵触,而且这时候用的还是px不须要强转rem谢谢 !

August 29, 2022 · 1 min · jiezi

关于vue.js:vue3响应式分析

vue3响应式剖析首先对vue3响应式解析之前,须要前置常识Proxy和Reflect有所理解,,对于这两个常识我举荐看阮一峰老师的ES6入门教程vue3的响应式我是以reactive为入口进行梳理,流程如下图源码地位:reactivity/src/...,分四局部解析 reactive文件:指标对象转化为proxy实例baseHandler文件:根本类型处理器collectionHandlers文件:Map、Set处理器effect文件:收集触发依赖如果不想看源码解析,能够间接看总结reactivereactive:将一个JS对象转为具备响应式的proxy实例 export function reactive(target: object) { // 如果是只读数据,就间接返回 if (target && (target as Target)[ReactiveFlags.IS_READONLY]) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap )}createReactiveObject function createReactiveObject( target: Target, // 源对象 isReadonly: boolean, // 是否只读 baseHandlers: ProxyHandler<any>, // 根本类型的handlers collectionHandlers: ProxyHandler<any>, // 次要针对(set、map、weakSet、weakMap)的handlers proxyMap: WeakMap<Target, any>) {// 如果不是一个对象,间接返回,并且在开发环境收回正告 if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // 如果曾经是响应式,间接返回 if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // 如果指标对象曾经存在代理,间接返回 const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // 如果类型值不是Object、Array、Map、Set、WeakMap、WeakSet的,间接返回 const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } // 依据不同的类型值赋予不同的handlers,就是我之前图上画的离开解决 /* 把set、Map这种数据与根底数据离开解决,是因为Map、Set中存储的数据必须通过this进行拜访 然而被proxy劫持后,this就变成了proxy, 所以须要非凡解决,把劫持办法进行重写 */ const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy}baseHandlerbaseHandler次要剖析reactive的处理器对象mutableHandlers ...

August 29, 2022 · 7 min · jiezi

关于vue.js:抛开脚手架搭建vue工程

平台的各位大佬专家们,大家好,自己是以后平台的小萌新。本期给大家带来第一篇文章——抛开脚手架搭建vue工程我的项目搭建是一个很常见的需要,为了解决这个需要。vue 官网也做了很多的致力,比方: vue-cli, vite等等。各位是不是认为博次要把vue-cli和vite的文档给拿过去,抄一遍。no,自己抱着分享技术而且,官网外面有的,大家能够去参考对应的文档: vue-cli: https://cli.vuejs.org/zh/guide/vite: https://cn.vitejs.dev/guide/在下面的两个官网地址中会有具体的阐明。 这里来带着大家来用最简略安装包的形式,一步一步实现一个可用于测试包的我的项目环境,简略的说就是搭建一个十分轻量级的我的项目,因为在年限不多的大部分程序员中,都会想要有一个轻量级的demo来进行深度学习和开发。当然,有的会问这个有啥作用。如果你公布一个包并且须要写测试案列。这种我的项目就会十分有用,毕竟大家都不想要有一个那么重的环境。 vue2搭建请思考一个问题,搭建一个vue工程化的我的项目,起码应用几个包就能实现? 有人可能会说3个5个啥的,答案先不焦急。请跟着节奏往下看。 剖析搭建vue的工程项目, vue必定是少不了的;vue工程外面写的是vue的模板文件或者是jsx,当然您也能够应用原生的js,应用h和render函数的形式来编写代码,然而一般来说工程化都会应用额定的语法来编写便于保护的代码。所以这里咋们还须要一个complier(编译器)既然是前端的工程化的我的项目,那必定也会应用构建工具了,如:webpack,vite 等.有了这3步操作,vue的工程化我的项目就能够搭建起来了。快来一起来入手试试吧搭建对于vue2的我的项目,咋们来装置下上面的包: vue: vue2代码的外围包vue-template-compiler: 编译vue代码的包@vue/cli-service: 构建工具,这里有人说不必webpack,vue-cli外面就是继承了webpack哦!npm install vue@2 -Snpm install vue-template-compiler @vue/cli-service -D装置实现后,来建设一些文件夹: 在根目录上面建设src和public两个文件,而后建设对应的文件。最终的目录构造如下: vue2-simple-pro // 项目名称├─ package.json // 启动入口├─ npm-lock.yaml // 依赖锁文件├─ public // 公共文件,不会参加打包的│ └─ index.html // 最终渲染的html文件└─ src // 外围性能文件 ├─ App.vue // 根节点文件 └─ main.js // 入口js,挂载vue的节点有了目录,要想让这个性能跑起来也是很简略的哦! package.json中增加启动和打包命令 "script":{ "serve": "vue-cli-service serve", "build": "vue-cli-service build"}index.html 外面就是须要蕴含一个<div id="app"></div> 即可 <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"></head><body> <div id="app"></div> <!-- built files will be auto injected --></body></html>App.vue 外面要蕴含的内容就更简略了,只须要一个vue2的模板即可 ...

August 28, 2022 · 2 min · jiezi

关于vue.js:Vue3Vite3-SSR基本搭建

Vue3+Vite3 SSR根本搭建首先阐明如果是生产应用强烈推荐Nuxt,然而如果想深刻服务端渲染的运行原理,能够看本篇,会依据渲染流程搭建一个demo版ssr,源码在最初会贴上次要技术栈:Vite3 + Vue3 + pinia + VueRouter4 + express开始搭建之前,先说一下SSR渲染流程SSR渲染流程首先浏览器向服务器申请,而后服务器依据申请的路由,会匹配相干的路由组件,而后执行组件的自定义服务端生命周期(例:Nuxt的asyncData)或者自定义获取数据的hook,并且把执行后的数据收集起来,对立在window的属性中存储而后vue的组件会被renderToString渲染成动态HTML字符串,替换掉index.html的提前指定的占位代码。而后index.html扭转后的动态字符串发给客户端客户端拿到后,首先对数据进行初始化,而后进行激活,因为以后html只是静态数据,激活次要做两件事 把页面中的DOM元素与虚构DOM之间建立联系为页面中的DOM元素增加事件绑定1. 创立我的项目首先用vite命令创立我的项目pnpm create vite vue-ssr --template vue-ts 装置相干依赖:pnpm add express pinia vue-router@4创立三个文件 touch server.js src src/entry-client.ts src/entry-server.js server.js:服务端启动文件entry-client.ts:客户端入口,利用挂载元素entry-server.js:服务端入口,解决服务端逻辑和动态资源批改package.json运行脚本 "scripts": { "dev": "node server", // 运行开发环境}而后须要把利用创立都改为函数的形式进行调用创立,因为在SSR环境下,和纯客户端不一样,服务器只会初始化一次,所以为了避免状态净化,每次申请必须是全新的实例 // src/main.tsimport { createSSRApp } from 'vue'import App from './App.vue'import { createRouter } from './router'import { createPinia } from 'pinia'export function createApp() { const app = createSSRApp(App) const router = createRouter() const pinia = createPinia() app.use(router) app.use(pinia) return { app, router, pinia }}router同理 ...

August 27, 2022 · 4 min · jiezi

关于vue.js:关于vue中配置代理跨域不生效的问题

前端小白,最近在写一个我的项目的登陆界面时,发送的申请始终都是在申请本地的vue.config.js里的配置是这样的这里是发送的申请拦截器然而发送的申请始终都是localhost有大佬能够帮忙解释一下吗

August 27, 2022 · 1 min · jiezi

关于vue.js:Vue响应式原理探究之发布订阅模式

前言在面试题中常常会呈现与“公布订阅”模式相干的题目,比方考查咱们对Vue响应式的了解,也会有题目间接考验咱们对“公布订阅”模式或者观察者模式的了解,甚至还会有一些手写算法题。笔者就在往年三月加入某平安公司的面试时被要求手写代码实现“公布订阅”模式,过后因为没有筹备没有答复上来,悔不该当初。由此可见“公布订阅”模式是一个十分重要的设计模式,接下来咱们一起学习下吧。 观察者模式 vs “公布订阅”模式首先须要廓清的是,这两者尽管类似,却有不同。 观察者模式只波及两个要害角色,发布者与订阅者。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个指标对象,当这个指标对象的状态发生变化时,会告诉所有观察者对象,使它们可能自动更新。 发布者的行为:减少订阅者移除订阅者告诉所有订阅者代码实现如下: // 定义发布者类 class Publisher { constructor() { // 创立订阅者 this.observers = [] } // 减少订阅者 add(observer) { this.observers.push(observer) } // 移除订阅者 remove(observer) { this.observers.map((item, i) => { if (item === observer) { this.observers.splice(i, 1) } }) } // 告诉所有订阅者 notify() { this.observers.map((observer) => { observer.update(this) }) }}订阅者行为:被告诉去执行代码实现: // 定义订阅者类 class Observer { constructor() { console.log('创立订阅者') } update() { console.log('订阅者更新') }}“公布订阅”模式与观察者模式相似,然而在这种模式下发布者齐全不必感知订阅者,不必关怀它怎么实现回调办法,事件的注册和触发都产生在独立于单方的第三方平台(事件总线)上。公布-订阅模式下,实现了齐全地解耦。 ...

August 26, 2022 · 2 min · jiezi

关于vue.js:VueElementui实现多级动态表头

业务性能: 依据开始,完结月份抉择实现动静表头 实现页面: 后端返回数据格式:data = [ { "行业分类": "建筑业", "202101": { "数量": 6, "占比": 20 }, "202102": { "数量": 5, "占比": 20 }, "202103": { "数量": 3, "占比": 18 }, "202104": { "数量": 4, "占比": 29 }, }, { "行业分类": "租赁和商务服务业", "202101": { "数量": 3, "占比": 10.0 }, "202102": { "数量": 3, "占比": 20.0 }, "202103": { "数量": 2, "占比": 20.0 }, "202104": { "数量": 4, "占比": 20.0 }, }, ]思路后端依据前端申请月份数据返回数据取出一级表头数据 ...

August 26, 2022 · 2 min · jiezi

关于vue.js:记一次Vue引入腾讯地图marker自定义图片不显示

一个我的项目应用腾讯地图之前应用的是v2的版本 marker自定义icon没什么问题 因为我的项目整体格调是暗色系 须要自定义地图款式 v2版本没有此性能只能切换回v1版本 因为代码逻辑在 革新的难度不是很大 遇到的问题就是 写了marker styles的src属性然而并没有失效 看了文档 说格局是url或者base64 将图片转成base64后也没有显示因为我是循环增加的最开始是循环调用的new TMap.MultiMarker这个办法 然而官网给的例子是geometries参数是整个的数组 于是我把坐标信息整合成数组传进去geometries 发现也不行打印整个marker发现其中有个default是地图默认的点标记款式 想着如果把这个改成要设置的图标会不会有成果 一试还真能够 我把地图整个的逻辑贴出来 if(document.getElementById('container').innerHTML != ""){//清空map容器 document.getElementById('container').innerHTML == "" } let center = new TMap.LatLng(this.centerLat,this.centerLng) this.map = new TMap.Map(document.getElementById('container'), { center: center,//设置地图中心点坐标 zoom: 15,//缩放等级 mapStyleId: 'style2'//这里是自定义地图的款式名须要在控制台配置 }); let geoArr = []//坐标数组 let time = 0 //用来判断是否循环结束 let num = this.geoList.length//循环总次数 for(let n in this.geoList){//循环后盾数据 time +=1 geoArr.push({ 'id': this.geoList[n].geo_hash,//id 惟一标识反复不可增加 'stykeId': 'marker', 'position': new TMap.LatLng(this.geoList[n].lat,this.geoList[n].lng),//点标记地位 'properties':{ title: this.geoList[n].loc_title } }) } if(time == num){//如果循环实现 调用增加办法 var marker = new TMap.MultiMarker({ id: 'marker', map: this.map, styles: { "default": new TMap.MarkerStyle({//这里的marker改成default 换掉默认的款式 "width": 25, "height": 35, "anchor": { x: 16, y: 32 }, "src": this.geoIcon//写在data中应用require引入 }) }, geometries:geoArr//坐标数组 }) marker.on('click',this.clickMarker)//给marker绑定点击事件 }

August 26, 2022 · 1 min · jiezi

关于vue.js:vue树图组件封装

前言背景阐明当初有很多的插件来实现树图的需要。然而对于特定的需要,插件多多少少都有点小问题没法满足。于是我查看了一些插件,并相熟了其外面的原理,就筹备着手本人封装一个树图组件 原理阐明其实树图组件很好了解,就是通过递归本身调用本身来实现树图。再说明确一点,最好在头脑中有一个画面,更加便于了解和开发。1、就是咱们先定义一个组件,在这个组件外面把第一个节点画进去2、而后再这第一个节点上面进行递归调用本身组件。这样就能够实现像糖葫芦一样一串节点3、但这只是一串,并不是树,所以还须要在这个根底上,画节点的中央加上循环的画节点。这样就成了树了。总的来说先易后难,先把骨干了解分明,而后缓缓的往这个树干下面加树枝,这样一棵大树就画好了 效果图展现 具体设计步骤(我集体不太喜爱间接贴代码上来,所以我就尽量用文字和简要代码来写分明整个的设计流程)1、数据的格局 [{ label: '显示文字', pictype: '图标类型', children: [ { label: '显示文字', pictype: '图标类型', children: [ { label: '显示文字', pictype: '图标类型', }, { label: '显示文字', pictype: '图标类型', }, ] }, { label: '显示文字', pictype: '图标类型', children: [ { label: '显示文字', pictype: '图标类型', } ] }, ]}]2、数据格式解析这是获取的数据格式,个别由后端返回给前端,然而这样间接传给组件是不够的,因为须要思考到节点开展的高度,我封装的时候,为了让组件外部更加通俗易懂,就把高度这个属性在传进组件前就增加进去。计算格局如下:1、遍历数组,对于节点对象中的children属性的"所有叶子节点"数有多少来乘以节点的高度2、假如节点高度为70,以上图的数据格式为例,一个根节点对象中的children有两个节点对象,第一个子节点对象有2个叶子节点,第二个根节点对象有1个叶子节点,所以根节点的高度就是703,第一个子节点的高度就是702,第二个子节点的高度就是701,所有叶子结点的高度也是7013、函数补充// 计算高度,并增加height属性, nodeInfo是根节点 traveTree(nodeInfo) { let childrenInfo = nodeInfo.children; if (!childrenInfo || childrenInfo.length == 0) { nodeInfo.height = 77; } else { // 循环叶子节点,给每个叶子节点都赋值高度 childrenInfo.map((item) => { this.traveTree(item); }); // 每个节点的高度都由子节点的高度相加 nodeInfo.height = childrenInfo.reduce((preV, n) => { return preV + n.height; }, 0); } return nodeInfo;}3、组件外部代码 ...

August 26, 2022 · 2 min · jiezi