关于vue.js:vue-interceptors-拦截器

interceptor联合springboot session和token等, 来验证用户是否登录 //让axios反对跨域拜访和cookieaxios.defaults.withCredentials = true;axios.interceptors.request.use( config => { // const token = getCookie('名称');留神应用的时候须要引入cookie办法,举荐js-cookie config.data = JSON.stringify(config.data); config.headers = { 'Content-Type':'application/json;charset=UTF-8' } return config; }, error => { return Promise.reject(error); });//http response 拦截器axios.interceptors.response.use( response => { return response; }, error => { return Promise.reject(error) })后端 //用户名和明码正确分支n if(userList!=null && userList.size()>0){ // request.getSession().setAttribute("user", userList.get(0)); request.getSession().setAttribute("userid", userList.get(0).getUserid()); return "success"; }else{ //用户名或则明码谬误分支 return "error"; }还能够联合window.sessionStorage一起应用

March 21, 2023 · 1 min · jiezi

关于vue.js:vue-components-分层开发-组件复用

由Stu.vue 代码拆分为三个规范的component,StuList.vue, StuAdd.vue, StuEdit.vue app StuList, 含有el-table StuEdit,含有el-dialogStuAdd, 含有el-dialog 代码构造更清晰, 业务解决更加标准, 便于大我的项目保护和二次开发组件能够简略的了解为dom和js的组合, 但设计出一个部分的片段, 既能够开发出对应的组件, 而后在后续的业务应用时(每一次), 调用这个组件即可例如, jeecg框架的分页等性能就相当于一个组件 要害代码<StuEdit ref="stuEdit" /> getStu: function(bean){ //lsit组件-->[stuEdit]-->stuEdit--> quan.$refs.stuEdit.getStu(bean); }, showAdd: function(){ //-->App.vue-->[stuList, stuAdd]-->.stuAdd.addFlag=true; quan.$parent.$refs.stuAdd.addFlag=true; //quan.$parent.$refs.stuAdd.addFlag=true; },

March 21, 2023 · 1 min · jiezi

关于vue.js:vue-中的-template-是干嘛的

在Vue中, <template> 标签用于定义Vue组件的模板。模板是Vue组件的一部分,它用于定义组件的构造和内容,能够蕴含HTML、CSS和JavaScript代码。 <template> 标签中的内容通常是Vue模板语法,它容许咱们应用Vue的非凡语法来绑定数据、指令、事件等。Vue模板语法包含插值表达式、指令、事件绑定等等。 在Vue中, <template> 标签能够作为一个包裹元素,用于蕴含多个子元素。在组件中, <template> 标签通常用于定义组件的构造,例如: <template> <div class="my-component"> <h1>{{ title }}</h1> <p>{{ content }}</p> </div></template>在这个例子中, <template> 标签蕴含了一个<div>元素,它蕴含了一个<h1>元素和一个<p>元素,其中应用了插值表达式来绑定组件的数据。 总的来说, <template> 标签在Vue中表演了一个很重要的角色,它是定义Vue组件模板的必要元素之一,也是组件构造和内容的容器。 Q:vue 中的 <template> 是 js 语法 还是 vue 本人的语法?A:在Vue中,<template>标签中应用的语法是Vue本人的模板语法,它是一种非凡的HTML语法,用于绑定数据、指令、事件等。 Vue的模板语法应用了一些非凡的标记,例如双括号表达式({{ }})、v-bind指令、v-on指令等等。这些标记是Vue特有的语法,不能在原生的HTML中应用。 尽管Vue的模板语法看起来像JavaScript,但它不是JavaScript语法。Vue的模板语法是在运行时编译成JavaScript代码的,最终生成的是一段纯HTML和JavaScript的代码。因而,Vue的模板语法是Vue本人的语法,不是JavaScript语法。 须要留神的是,在Vue 3中,因为引入了Composition API,咱们能够应用JavaScript语法来编写组件的逻辑代码。然而,<template>标签中的语法依然是Vue本人的模板语法。 Q:vue 的最终输入是 html 吗?A:在Vue中,最终输入的并不是HTML,而是通过Vue编译器编译后生成的JavaScript代码。 在Vue中,咱们编写的Vue组件模板实际上是一种非凡的模板语法,它会被Vue编译器编译成JavaScript代码。这些JavaScript代码会通过Vue的虚构DOM算法进行比拟和渲染,最终生成真正的HTML代码,并将其渲染到浏览器中。 Vue的编译器是在运行时进行编译的,这意味着咱们能够在浏览器中应用Vue,而不须要提前编译模板。在开发环境下,Vue会主动在浏览器中编译模板。在生产环境下,咱们能够应用Vue的构建工具将模板预编译成JavaScript代码,以进步性能。 因而,尽管Vue组件模板看起来像是HTML代码,但实际上它最终被编译成JavaScript代码,并通过Vue的虚构DOM算法生成真正的HTML代码,而后渲染到浏览器中。 Q:一个 vue 文件,最多能够有几个 <template>?A:一个Vue文件中只能有一个根级别的 <template> 标签。这个 <template> 标签蕴含了整个组件的模板内容,它是组件模板的入口。 在 <template> 标签中,能够应用多个HTML元素来定义组件的构造和内容。如果须要在组件中定义多个模板,能够应用Vue的动静组件(<component>标签)来实现。动静组件能够依据组件的属性或状态来动静加载不同的组件,从而实现在一个Vue文件中定义多个不同的模板。 示例: <template> <div> <h1>{{ title }}</h1> <component :is="currentComponent"></component> </div></template><script> import Component1 from './Component1.vue' import Component2 from './Component2.vue' export default { data() { return { title: 'Dynamic Components', currentComponent: 'Component1' } }, components: { Component1, Component2 } }</script>在这个例子中,咱们应用了一个<component>标签来动静加载不同的组件。依据组件的属性或状态,咱们能够将currentComponent设置为'Component1'或'Component2',从而动静加载不同的组件模板。 ...

March 14, 2023 · 1 min · jiezi

关于vue.js:uniapp开发小程序系列微信登录

何为微信登录官网文档如是说:小程序能够通过微信官网提供的登录能力不便地获取微信提供的用户身份标识,疾速建设小程序内的用户体系。换句话说就是让用户在咱们小程序的用户 id 和用户的微信 open_id 相关联。这样用户拜访时,咱们能够通过获取用户的 open_id,从而晓得用户在咱们平台的 id。这样用户就能够不必输出账号密码去登录本人的账号了。 登录流程整个登录流程官网文档中也给了一个时序图,如下:须要解释的是,开发者服务器就是咱们的后端服务。所以要在微信小程序中实现用户微信快捷登录,须要前后端的配合实现(全栈开发除外)。前端侧次要实现的过程是图中的 1,2,6,7 四个步骤。 具体实现有了上边的时序图,咱们实现登录逻辑其实就比较简单了,照着流程做就是了。第一步当然是要领有一个微信小程序我的项目,笔者应用了 uni-app 初始化的我的项目,还没有生成我的项目的小伙伴能够移步我的另一篇博客:uni-app 开发小程序系列--我的项目搭建 何时触发用户登录?何时触发用户登录?这个中央不同的利用有不同的要求和设计。在笔者看来,因为登录过程其实是静默的,用户无感知的,也未波及获取用户的隐衷数据,所以在用户间接拜访时就立马登录生成用户会话态是最好的。这样咱们也好剖析访客的数据。在 uni-app 我的项目中,有个根组件 App.vue,咱们能够抉择在这里 onLaunch 生命周期函数中去做登录逻辑解决。笔者代码如下: <script>export default { onLaunch: function() { // 从storage获取登录信息,没有则须要登录 let tokenInfo = uni.getStorageSync("tokenInfo"); let hasValidToken = false; if (tokenInfo) { let time = new Date().valueOf(); // 存储工夫小于token生效工夫,才是无效token, 否则从新受权 hasValidToken = time - tokenInfo.timestamp < 3600 * 24 * 1000; } if (!hasValidToken) { // 调用小程序登录api uni.login({ provider: "weixin", success: (wxInfo) => { // 获取到code后,提交给服务端 this.$api.post('/wxa/login', { code: wxInfo.code, }).then((res) => { // 存储获取到的token uni.setStorage({ key: 'tokenInfo', data: { token: res.token, timestamp: new Date().valueOf() } }) }) }, }); } }, onShow: function() { console.log('App Show') }, onHide: function() { console.log('App Hide') }}</script>上边代码曾经蕴含了时序图中的 1,2,6 步骤。有几个中央须要解释一下: ...

March 14, 2023 · 3 min · jiezi

关于vue.js:手把手教你vue项目接入漂亮的验证码

前言本文教你基于Node.js环境,在vue我的项目中如何接入KgCapctah。 筹备工作拜访凯格行为验证码官网,注册账号后登录控制台,拜访“无感验证”模块,申请开明后零碎会调配给利用一个惟一的AppId、AppSecret。凯格提供后端SDK来校验token(即平安凭据)是否非法 ,目前反对PHP版、Python版、Java/JSP版、.Net C#版。拜访Node.js官网,下载Node.js运行环境,拜访vue.js中武官网,装置下载vue.js,创立一个vue我的项目,具体操作请查看vue.js中武官网我的项目目录 代码实现index.html 我的项目根目录index.html文件,头部援用凯格行为验证码js<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><!--引入凯格行为验证码js--><script id="KgCaptcha" src="https://cdn.kgcaptcha.com/captcha.js?appid=XXX"></script><!--引入凯格行为验证码js--></head><body> <!--vue主体--> <div id="app"></div> <!--vue主体--></body></html>main.js src/main.js文件中,配置路由import Vue from 'vue'import App from './App'import router from './router'Vue.config.productionTip = false// 配置全局路由、组件new Vue({ el: '#app', router, components: { App }, template: ''})App.vue src/App.vue文件中,定义html<template> <div id="app"> <!--自定义组件、内容--> <form id="form"> token: <input name="token" _cke_saved_name="token" _cke_saved_name="token" _cke_saved_name="token" id="token"> <!--凯格行为验证码组件--> <div id="captchaBox"></div> <!--凯格行为验证码组件--> <button type="submit">提交</button> </form> <!--自定义组件、内容--> </div></template> <script>export default { name: 'App',}//初始化凯格行为验证码kg.captcha({ // 绑定元素,验证框显示区域 bind: "#captchaBox", // 验证胜利事务处理 success: function(e) { console.log(e); kg.$('#token').value = e['token'] }, // 验证失败事务处理 failure: function(e) { console.log(e); }, // 点击刷新按钮时触发 refresh: function(e) { console.log(e); }});</script>成果展现 ...

March 13, 2023 · 1 min · jiezi

关于vue.js:网页重新部署通知用户最佳实践

为什么须要如果用户打网页后,长时间不敞开对应标签页,也不刷新页面(在中后盾治理我的项目挺常见的),且期间服务器页面有更新,可能会存在以下问题: 单页利用,如果前端部署是全量更新的话(倡议增量更新),用户跳转其余页面,可能会 404, 因为对应资源曾经不存在了。如果后端接口有更新,且没做兼容解决的话,会影响数据准确性,也可能间接报错。一个紧急 bug,你修复并公布了,用户如果没刷新页面,还是能够复现。用户长期应用历史版本,影响用户体验。老板和销售:不是说好的明天更新吗,怎么页面还是老样子。你:刷新下页面就好了老板和销售:怎么刷新页面啊?你:plugin-web-update-notification监听网页更新,并告诉用户刷新页面的插件,反对 Vite、Webpack、umi没错,最佳实际就是这个插件。 github | npm | 文档 原理以 git commit hash (也反对 package.json version、build timestamp、custom) 为版本号,打包时将版本号写入一个 json 文件,同时注入客户端运行的代码。客户端轮询服务器上的版本号(浏览器窗口的visibilitychange、focus 事件辅助),和本地作比拟,如果不雷同则告诉用户刷新页面。 长处接入简略,装置插件,批改配置文件即可,不必批改业务代码(如果不自定义行为)。 // 以 vite 为例import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'import { webUpdateNotice } from '@plugin-web-update-notification/vite'// https://vitejs.dev/config/export default defineConfig({ plugins: [ vue(), webUpdateNotice({ logVersion: true, }), ]})反对多种版本号类型: git commit hash(默认)。package.json 中的 version 字段。(倡议搭配 bumpp 食用最佳)build timestamp,运行打包命令时的工夫戳。custom,用户自定义版本号。预置了一个简洁的更新 Notification,能够自定义文案、款式、地位。当然也能够勾销默认的 Notification,监听到更新事件后自定义行为。 预置的 Notification 文案反对国际化。tiny, 注入的 js 和 css 文件压缩后不到 2kb。欠缺的 ts 类型提醒。issue 响应急时...什么时候会检测更新检测:通过 fetch 加载服务器上的 version.json 文件(会疏忽本地缓存)。首次加载页面。轮询(default: 10 60 1000 ms)。js 脚本资源加载失败 (404 ?)。标签页 visibilitychange、focus 事件为 true 时。(按目前公司的我的项目来看,大多数更新是这个时候命中的,轮询反而少些)对于时效性有人说轮询如果频率太慢了,时效性会比拟差。 ...

March 12, 2023 · 2 min · jiezi

关于vue.js:新手小白都能看懂的uniapp小程序项目搭建流程

搭建步骤1. 装置HBuilder编辑器HBuilder是uni-app官网团队专门定制的编辑器,它对Vue做了大量优化投入,且反对uni-app官网库Api的智能提醒和推断,同时,咱们也能够在通过编辑器疾速的创立各种场景下的我的项目模板,总之HBuilder是用uni-app进行利用开发的首选编辑器,能够拜访其官网进行下载安装,点击如下链接可间接跳转:Hbuilder官网下载地址 2. 初始化我的项目装置好HBuilder后,关上编辑器,点击左上角菜单“文件”->"新建",抉择“我的项目”,即可进入初始化我的项目的设置界面,界面大略如下: 抉择我的项目类型:按默认选uni-app就行了,其余几种类型基本上和做小程序都无关,咱们不必关怀;填写项目名称:倡议用英文命名(当然非要用中文应该也不是不能够);抉择我的项目所在门路:倡议选一个本人常常记得分明的目录,省得日后找我的项目所在位置麻烦;抉择模板:uniapp提供了很多针对不同场景的示例模板,老手的话,倡议默认模板就好了,内容少一点,咱们能够缓缓加减性能;Vue版本抉择:都2023年了,当然抉择Vue3了,毕竟Vue3是兼容Vue2的;启用UniCloud:这个是Uniapp集成的云开发,老手能够不开启;上传代码到托管平台:也就是将我的项目源码进行git托管,这里也能够不必抉择,须要托管的话,咱们能够后续再本人抉择。配置完后,点击确定,我的项目就生成好了,能够看到生成的我的项目目录如下:最外围的目录pages内,能够看到只有一个页面index, 那么怎么运行呢?因为咱们是要开发一个微信小程序,而微信小程序官网是有提供一个开发和调试工具的,所以如果须要让我的项目跑起来,咱们还得装置一下这个微信开发者工具。 3. 装置并配置微信开发者工具微信官网提供的小程序开发文档中有提供微信开发者工具的下载地址,点击如下链接可间接跳转:微信开发者工具下载地址抉择一个对应本人操作系统的版本装置就好了。装置好后,关上软件左上角菜单中的设置,抉择平安设置而后把服务端口设为开启状态。开启服务端口是便于HBuilder能主动帮咱们关上微信开发者工具的,很多老手也会卡在这一步,发现运行我的项目的时候没有反馈,其实就是因为这里的服务端口未开启。 4.启动我的项目微信开发者工具配置好后,咱们切会HBuilder,抉择左上角菜单区的“运行”->"运行到小程序模拟器"->"微信开发者工具"。或者间接点下边的运行图标,抉择微信开发者工具。点击后等一会,微信开发者工具就会主动被关上且加载咱们的我的项目。留神:第一次运行时因为会装置一些依赖文件,会提醒让咱们从新运行。这时从新点击一次就能够了。失常启动后,控制台显示大抵如下:微信开发者工具中,也能看到这个模板我的项目的界面模样:咱们能够尝试找到pages/index/index.vue文件,将data中的title设置为“Hello World”。编辑好后保留,再切回微信开发者工具,能够看到页面也会进行热更新的变动。 <template> <view class="content"> <image class="logo" src="/static/logo.png"></image> <view class="text-area"> <text class="title">{{title}}</text> </view> </view></template><script> export default { data() { return { title: 'Hello World' } }, onLoad() { }, methods: { } }</script>HBuilder配置批改缩进长度为了更加的不便开发,咱们能够对HBuilder做一些让开发体验更好的设置。笔者喜爱2个空格的缩进,HBuilder默认是四个空格的。咱们能够在左上方菜单中找到“偏好设置”->“罕用设置”,再找到制表符长度,将4设置为2。 保留时主动格式化代码HBuilder自带了代码格调的格式化性能,默认未开启保留时主动格式化。咱们能够在“偏好设置”->“编辑器配置”中找到「保留时主动格式化」,勾选上 设置大括号保留不换行笔者试图将Script中代码批改为Vue3的setup写法,如下:能够看到,缩进配置和保留后主动格式化都曾经失效了,然而导入ref的那行代码,大括号主动换行了。这是因为HBuilder内的代码格式化插件jsbeautify默认设置的成果。咱们能够批改它来让他保留不换行。咱们能够在“偏好设置”->“插件配置”中找到「关上文件jsbeautifyrc.js」,点击关上,而后找到brace_style属性,把值改为"brace_style": "collapse,preserve-inline"而后保留回到原页面,将大括号的缩进去掉再保留,能够发现主动格式化就不会让大括号再换行了。接下来,咱们即可舒舒服服的编写代码啦! 对于笔者计算机专业科班出身,8年+Web开发教训,多年深耕Vue2,Vue3技术栈。全栈开发经验丰富,技能树笼罩从前端工程搭建到部署上线全链路流程。紧跟技术潮流,始终关注着各种新兴技术趋势并踊跃进行实际摸索,谋求优雅的开发体验,极致的开发效率,高标准的开发品质。可提供前端简历批改,面试领导,前端新人开发领导,前端收徒,帮助实现开发工作等服务。 分割我:imwty2023(微信) 博客原创地址:uni-app开发小程序系列--我的项目搭建 | imwty,转载请注明出处。

March 12, 2023 · 1 min · jiezi

关于vue.js:使用-Vue-3-时应避免的-10-个错误

Vue 3曾经稳固了相当长一段时间了。许多代码库都在生产环境中应用它,其他人最终都将不得不迁徙到Vue 3。我当初有机会应用它并记录了我的谬误,上面这些谬误你可能想要防止。 应用Reactive申明原始值数据申明在过来都是十分间接的,然而当初有很多帮忙函数供咱们应用。目前的规定是: 应用reactive申明Object, Array, Map, Set应用ref申明String, Number, Boolean为一个原始值应用reactive会返回一个正告,并且该值不会成为可响应式数据。 /* DOES NOT WORK AS EXPECTED */<script setup>import { reactive } from "vue";const count = reactive(0);</script>矛盾的是,另一种形式是可行的。例如,应用ref来申明一个Array会在外部调用reactive。 解构响应式数据假如你有一个响应式对象领有count属性,并且有一个按钮来递增count。 <template> Counter: {{ state.count }} <button @click="add">Increase</button></template><script>import { reactive } from "vue";export default { setup() { const state = reactive({ count: 0 }); function add() { state.count++; } return { state, add, }; },};</script>上述逻辑相当间接,而且如预期的那样工作,但你可能会利用javascript的解构来做以下事件: /* DOES NOT WORK AS EXPECTED */<template> <div>Counter: {{ count }}</div> <button @click="add">Increase</button></template><script>import { reactive } from "vue";export default { setup() { const state = reactive({ count: 0 }); function add() { state.count++; } return { ...state, add, }; },};</script>代码看起来是一样的,而且依据咱们以前的教训应该是可行的,但事实上,Vue的响应式跟踪是通过属性拜访进行的。这意味着咱们不能赋值或解构一个响应式对象,因为与第一个援用的响应式连贯曾经断开。这就是应用响应式帮忙函数的局限性之一。 ...

March 10, 2023 · 2 min · jiezi

关于vue.js:vue-几乎都会用到的组件库

element: https://element.eleme.cn/#/zh-CN没什么好说的 echarts: https://echarts.apache.org/zh/index.html也没社么好说的,做过图表的都用过啊吧 vxe-table: https://vxetable.cn/#/table/start/install组件的确要比element功能强大,然而官网文档写的不如element友善,常常有指令或属性找不到,示例也不是很残缺。 draggable: https://github.com/SortableJS/Vue.Draggable能够用鼠标间接拖动的穿梭框,能够实现的成果比 element 不要难看太多中文文档能够参照:https://www.itxst.com/vue-draggable/tutorial.html代码片段示例: <!--左侧穿梭框--> <el-col :span="12"> <div class="groupbox"> <div class="title"> <el-col :span="12">姓名</el-col> <el-col :span="12">职务</el-col> </div> <div class="listBody"> <draggable v-model="leftarr" group="itxst" dragClass="dragClass" ghostClass="ghostClass" chosenClass="chosenClass"> <transition-group class="groutminheight"> <div class="item" v-for="item in leftarr" :key="item.name"> <el-row> <el-col :span="12">{{ item.name }}</el-col> <el-col :span="12">{{ item.post }}</el-col> </el-row> </div> </transition-group> </draggable> </div> </div> </el-col> <!--右侧穿梭框--> <el-col :span="12"> <div class="groupbox"> <div class="title"> <el-col :span="12">姓名</el-col> <el-col :span="12">职务</el-col> </div> <div class="listBody"> <draggable v-model="reartarr" group="itxst" dragClass="dragClass" ghostClass="ghostClass" chosenClass="chosenClass"> <transition-group class="groutminheight"> <div class="item" v-for="item in reartarr" :key="item.name"> <el-row> <el-col :span="12">{{ item.name }}</el-col> <el-col :span="12">{{ item.post }}</el-col> </el-row> </div> </transition-group> </draggable> </div> </div> </el-col><style>//定义穿梭框款式.ghostClass { background-color: blue !important;}//鼠标选中后的色彩.chosenClass { background-color: #2DF67D !important; opacity: 1 !important;}//拖动时的款式.dragClass { background-color: greenyellow !important; opacity: 1 !important; box-shadow: none !important; outline: none !important; background-image: none !important;}//包含题目合列表的容器盒子.groupbox{ margin:20px; padding: 10px; background:linear-gradient(to top,rgba(65,216,147,0.3) 0%,rgba(65,216,147,0.7) 50%, rgba(65,216,147,0.3) 100%); display:flex; flex-direction: column;}//题目.title{ font-size: 18px; height: 35px; margin-left: 20px; margin-right:20px; margin-bottom: 8px; top:0px; flex: 0 0 35px;}//列表容器盒子.listBody{ flex:1 1 200px; overflow: auto;}//列表.groutminheight{ overflow: auto; display: block; min-height:100px;}//暗藏滚动条::-webkit-scrollbar { display: none; /* Chrome Safari */}.item { padding: 6px 12px; margin: 0px 10px 0px 10px; background-color: #f1f1f1; border-radius: 15px;}.item:hover { background-color: #fdfdfd; cursor: move;}.item + .item { border-top: none; margin-top: 6px;}</style>效果图: ...

March 8, 2023 · 1 min · jiezi

关于vue.js:elementUI之tablefixed列后如何修改固定列的背景

// 外围,批改无固定列的背景色彩。tbody tr:hover>td { background-color: rgba(0,0,0,0) !important //批改成本人想要的色彩即可}.el-table__fixed{ // 隔行的row-class-name .row-blueBg{ background-color: rgba($color: #386DD8, $alpha: 1); color: #FFF; // 外围,批改被固定的列的背景色彩。 &.hover-row > td.el-table__cell { background-color: rgba($color: #386DD8, $alpha: 1); } }}

March 6, 2023 · 1 min · jiezi

关于vue.js:vue3基础语法涉及部分功能demo

业余时间抽空整顿了一下vue3的局部根底语法。整顿谈不上高大上,集体认为比拟适宜以下状况的同学:技术类型老鸟(高端的代码敲多了,根底语法有的时候太久没写忘了想回味一下(自己就是))相熟vue2的人员,想略微过一下vue3语法想间接从vue3学起的同学话不多说,间接上我的项目图giteeremark: 我的项目仍在继续更新中,有趣味的同学能够珍藏仓库地址如感觉我的项目很有必要加上某模块,能够留言That's all.

March 6, 2023 · 1 min · jiezi

关于vue.js:实现从0搭建基于Vite的Vue项目

之前试过从0搭建一个基于webpack的Vue我的项目,当初再尝试下从0搭建一个基于vite的Vue我的项目,目标在于相熟Vite的流程,不然咱们在应用webpack或者vite的时候,对整个过程都是不清不楚的。 初始化创立文件夹,并用vscode关上 npm init -y装置依赖npm i vite --savenpm i vue@next --save // 目前vue默认装置版本还是2 所以咱们在装置的时候须要指定版本npm i @vitejs/plugin-vue --save--devnpm install vuex@next --savenpm install vue-router@4.0 创立必要的文件夹以及文件src/App.vuesrc/index.htmlsrc/main.tsvite.config.jssrc/router/index.tssrc/shims-vue.d.tsindex.html参考官网,index.html须要放到根目录;如果不放在根目录也能够,须要在vite.config.ts中配置root即可。 <script type="module" src="./src/main.ts"></script>main.tsimport { createApp } from 'vue'import App from './App.vue'import router from './router'const app = createApp(App);app.use(router);app.mount('#app');vite.config.tsimport { defineConfig } from 'vite';import vue from '@vitejs/plugin-vue';export default defineConfig({ plugins: [vue()],})package.json增加执行命令,启动我的项目 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "serve": "vite" },

March 2, 2023 · 1 min · jiezi

关于vue.js:裁剪上传图片

应用vue3和elementPlus实现上传图片裁剪参考文章地址:https://chengpeiquan.com/article/vue-picture-cropper.html#demo <template> <!-- 抉择图片 --> <input ref="uploadInput" type="file" accept="image/jpg, image/jpeg, image/png, image/gif" @change="selectFile" /> <!-- 抉择图片 --> <!-- 后果预览区 --> <div v-if="result.dataURL && result.blobURL"> <p>裁切后的 Base64 图片预览:</p> <div class="preview"> <img :src="result.dataURL" alt="组合式 API" /> </div> <p>裁切后的 Blob 图片预览:</p> <div class="preview"> <img :src="result.blobURL" alt="组合式 API" /> </div> <p>能够按 F12 查看打印的 base64 / blob / file 后果</p> </div> <!-- 后果预览区 --> <!-- 用于裁切的弹窗 --> <el-dialog v-model="isShowDialog" title="图片裁切" :close-on-click-modal="false"> <template #footer> <el-button @click="isShowDialog = false">勾销</el-button> <el-button @click="clear">革除</el-button> <el-button @click="reset">重置</el-button> <el-button type="primary" @click="getResult">裁切</el-button> </template> <!-- 图片裁切插件 --> <VuePictureCropper :boxStyle="{ width: '100%', height: '100%', backgroundColor: '#f8f8f8', margin: 'auto', }" :img="pic" :options="{ viewMode: 1, dragMode: 'crop', aspectRatio: 16 / 9, }" /> <!-- 图片裁切插件 --> </el-dialog> <!-- 用于裁切的弹窗 --></template><script setup>import { reactive, ref } from 'vue'import VuePictureCropper, { cropper } from 'vue-picture-cropper'const isShowDialog = ref(false)const uploadInput = ref(null)const pic = ref('')const result = reactive({ dataURL: '', blobURL: ''})/** * 抉择图片 */const selectFile = (e) => { // 重置上一次的后果 result.dataURL = '' result.blobURL = '' // 如果有多个裁剪框,也须要重置掉裁剪指标的值,防止应用同一张图片无奈触发watch pic.value = '' // 获取选取的文件 const target = e.target const { files } = target if (!files) return const file = files[0] // 转换为base64传给裁切组件 const reader = new FileReader() reader.readAsDataURL(file) reader.onload = () => { // 更新裁切弹窗的图片源 pic.value = String(reader.result) // 显示裁切弹窗 isShowDialog.value = true // 清空已抉择的文件 if (!uploadInput.value) return uploadInput.value.value = '' }}/** * 获取裁切后果 */const getResult = async () => { console.log(cropper) // 获取生成的base64图片地址 const base64 = cropper.getDataURL() // 获取生成的blob文件信息 const blob = await cropper.getBlob() // 获取生成的file文件信息 const file = await cropper.getFile({ fileName: '裁剪头像', }) console.log({ base64, blob, file }) // 把base64赋给后果展示区 result.dataURL = base64 try { result.blobURL = URL.createObjectURL(blob) } catch (e) { result.blobURL = '' } // 暗藏裁切弹窗 isShowDialog.value = false}/** * 革除裁切框 */const clear = () => { cropper.clear()}/** * 重置默认的裁切区域 */const reset = () => { cropper.reset()}</script><style scoped></style>

March 2, 2023 · 2 min · jiezi

关于vue.js:离线环境安装vue脚手架

最近做一个我的项目,必须在离线的环境下开发,后果装置@vue/cli捣鼓了半天。网上对于此的探讨并不多,那些他人提供的办法也并没有胜利。我来记录下怎么做的。 筹备一台联网的电脑,给离线的电脑装置好node,两台电脑的零碎、node和npm版本要统一在联网的电脑上装置好@vue/cli,版本要是你vue我的项目里用到的哦在联网的电脑上装置npm-pack-allnpm install npm-pack-all -g而后找到你装置好的@vue/cli地址,应用npm root -g找到全局装置地位cd xxxx/node_modules/@vue/cli应用npm-pack-all命令打包,生成一个vue-cli-x.xx.tgz文件,x.xx是版本而后将这个包拷贝到内网的电脑上,在这个包的地址关上终端,而后装置这个包npm install vue-cli-x.xx.tgz -g而后你就能够试试在内网的电脑上运行你的vue我的项目了。这个办法也能够用来在我的项目里离线装置依赖。

March 1, 2023 · 1 min · jiezi

关于vue.js:拥抱下一代前端工具链Vue老项目迁移Vite探索

作者:京东物流 邓道远 背景形容随着我的项目的一直保护,代码越来越多,我的项目越来越大。调试代码的过程就变得极其苦楚,期待我的项目启动的工夫也越来越长,尤其是须要解决紧急问题的时候,切换我的项目启动,期待的工夫就会显得尤为的漫长。无法忍受这种开发效率的我,决定将老我的项目迁徙至vite。 间隔Vite工具公布到当初曾经有了一些日子了,工具链与生态曾经趋于稳定,最新版本曾经更新到了3.0,既然念头已起,心动不如口头。 1、什么是Vitevite 发音为/vit/ 法语中就是快的意思,“人”如其名,就是快 一个开发服务器,它基于原生ES模块,提供了丰盛的内建性能,如速度快到惊人的模块热更新(HRM)一套构建指令,它应用rollop来打包你的代码,并且是预配置的,可输入用于生产环境的高度优化过的动态资源。2、为什么快家喻户晓,当冷启动服务器时,基于打包器的启动必须优先抓取并构建你的整个利用,而后能力提供服务,这一抓取构建的过程随着文件越来越多,工夫也会越来越长。 而Vite却通过将利用中的木块辨别为依赖和源码两类,从而优化了大量的服务器启动工夫。 依赖大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)解决的代价也很高。依赖也通常会存在多种模块化格局(例如 ESM 或者 CommonJS)。Vite 将会应用 esbuild预构建依赖。esbuild 应用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。源码通常蕴含一些并非间接是 JavaScript 的文件,须要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都须要同时被加载(例如基于路由拆分的代码模块)。Vite 以原生 ESM形式提供源码。这实际上是让浏览器接管了打包程序的局部工作:Vite 只须要在浏览器申请源码时进行转换并按需提供源码。依据情景动静导入代码,即只在以后屏幕上理论应用时才会被解决。 3、如何实现老我的项目迁徙以后我的项目是Vue2.0,vue-cli4.0,node v14.18.2 3.1 首先咱们须要先明确我的项目构造与原来的Vue老我的项目相比,模板文件 index.html 须要从public挪到我的项目根目录中,Vite将 index.html 视为源码和模块图的一部分。因为咱们只有一个入口文件,所以在index.html中须要引入main.ts <script type="module" src="/src/main.ts"></script>而且运行过程中可能会遇到上面写法引发的报错 <link rel="icon" href="<%= BASE_URL %>favicon.ico" />[vite] Internal server error: URI malforme解决办法是能够写一个简略的插件替换一下 res = code.replace(/<%=\s+BASE_URL\s+%>/g, baseDir);与Vue-cli雷同,须要一个配置文件 vite.cofnig.js, 与原来的vue.config.js同级 3.2 装置依赖既然咱们应用Vite,那么咱们须要装置一个vite依赖。然而咱们的老我的项目是Vue2.0,vite优先反对Vue3.0,所以咱们还须要一个转换工具 "vite-plugin-vue2" npm i vite vite-plugin-vue2 -S3.3 批改配置文件批改package.json中的scripts,启动和打包形式应用vite "serve": "vite","build": "vite build",批改vite.config.js,与vue.config.js类似 ...

February 28, 2023 · 2 min · jiezi

关于vue.js:源码库Vue3-中的-nextTick-魔法背后的原理

在应用Vue的时候,最让人着迷的莫过于nextTick了,它能够让咱们在下一次DOM更新循环完结之后执行提早回调。 所以咱们想要拿到更新的后的DOM就上nextTick,想要在DOM更新之后再执行某些操作还上nextTick,不晓得页面什么时候挂载实现仍然上nextTick。 尽管我不懂Vue的外部实现,然而我晓得有问题上nextTick就对了,你天天上nextTick,那么nextTick为什么能够让你这么爽你就不好奇吗? 大家好,这里是田八的【源码&库】系列,Vue3的源码浏览打算,Vue3的源码浏览打算不出意外每周一更,欢送大家关注。 如果想一起交换的话,能够点击这里一起独特交换成长 系列章节:【源码&库】跟着 Vue3 学习前端模块化【源码&库】在调用 createApp 时,Vue 为咱们做了那些工作?【源码&库】细数 Vue3 的实例办法和属性背地的故事首发在掘金,无任何引流的意思。 nextTick 简介依据官网的简略介绍,nextTick是期待下一次 DOM 更新刷新的工具办法。 类型定义如下: function nextTick(callback?: () => void): Promise<void> {}而后再依据官网的具体介绍,咱们能够晓得nextTick的大体实现思路和用法: 当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步失效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论产生多少状态扭转,都仅执行一次更新。 nextTick()能够在状态扭转后立刻应用,以期待 DOM 更新实现。你能够传递一个回调函数作为参数,或者 await 返回的 Promise。 官网的解释曾经很具体了,我就不适度解读,接下来就是剖析环节了。 nextTick 的一些细节和用法nextTick 的用法首先依据官网的介绍,咱们能够晓得nextTick有两种用法: 传入回调函数nextTick(() => { // DOM 更新了})返回一个PromisenextTick().then(() => { // DOM 更新了})那么这两种办法能够混用吗? nextTick(() => { // DOM 更新了}).then(() => { // DOM 更新了})nextTick 的景象写了一个很简略的demo,发现是能够混用的,并且发现一个有意思的景象: const {createApp, h, nextTick} = Vue;const app = createApp({ data() { return { count: 0 }; }, methods: { push() { nextTick(() => { console.log('callback before'); }).then(() => { console.log('promise before'); }); this.count++; nextTick(() => { console.log('callback after'); }).then(() => { console.log('promise after'); }); } }, render() { console.log('render', this.count); const pushBtn = h("button", { innerHTML: "减少", onClick: this.push }); const countText = h("p", { innerHTML: this.count }); return h("div", {}, [pushBtn, countText]); }});app.mount("#app");我这里为了简略应用的vue.global.js,应用形式和Vue3一样,只是没有应用ESM的形式引入。运行后果如下: ...

February 28, 2023 · 4 min · jiezi

关于vue.js:vue脚手架多页自动化生成实践

前言在前端开发过程中,经常面对多种业务场景。到目前为止,前端对于不同场景的解决通常会采纳不同的渲染计划来组合解决,常见的渲染计划包含:CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generation)、ISR(Incremental Site Rendering)、DPR(Distributed Persistent Rendering)、NSR(Native Side Rendering)以及ESR(Edge Side Rendering)等。在目前我的项目开发过程中,遇到了须要构建门户类利用的需要,而团队次要技术栈以Vue为主,整个技术计划以Vue全家桶进行构建。因而,本文旨在针对门户类利用的场景下的Vue脚手架构建计划的一些总结和剖析,通过自动化的配置脚本来生成模板化的多页利用实际,以期可能给读者提供一个基于Vue全家桶的门户类工程构建计划。 架构 对于门户类型的利用,因为其大部分内容变动内容较少,而对于局部要害页面却会有动静更新的要求,因此在通常会采纳多页模式的解决配合局部单页利用中的劣势进行解决。因此,在技术选型方面,团队采纳了预渲染配合多页的形式实现门户类SEO及首屏加载快的需要。同时,联合单页利用的劣势,在多页中的局部要害页面中采纳单页中的长处,如:路由切换快、用户体验好等。综上,架构格调采纳ISR的增量渲染计划,因为我的项目背景的特殊性,无奈配合惯例CDN等部署计划特点,但能够应用云原生相干的中间件实现相似成果,整体部署仍以“云+端”的模式为主。 目录selfService├─portal├─ build // vue cli打包所需的options中内容一些抽离,对其中做了环境辨别| ├─ demo| | ├─config.json| | ├─configureWebpack.js| ├─ dev| | ├─ config.json| | ├─ configureWebpack.js| ├─ production| | ├─ config.json| | ├─ configureWebpack.js| ├─ chainWebpack.js| ├─ configureWebpack.js| ├─ devServer.js| ├─ pages.js| ├─ routes.js| ├─ index.js| ├─ utils.js├─ deploy // 不同环境的部署| ├─ demo| | ├─ default.conf| | ├─ Dockerfile| | ├─ env.sh| ├─ dev| | ├─ default.conf| | ├─ Dockerfile| | ├─ env.sh| ├─ production| | ├─ default.conf| | ├─ Dockerfile| | ├─ env.sh| ├─ build.sh├─ public| ├─ pageA // pageA的html,这里能够寄存一些动态资源,非构建状态下的js、css等| | ├─ index.html| ├─ pageB // pageB的html,这里能够寄存一些动态资源,非构建状态下的js、css等| | ├─ index.html| ├─ favicon.ico├─ src| ├─ assets // 寄存小资源,通常为必须,如:logo等,其余动态资源请放入cdn或者public下| | ├─ logo.png| ├─ components // 公共组件,可抽离多个动态页面的公共组件| | ├─ Header.vue| ├─ router| | ├─ pageA // pageA的router,应用了history模式| | ├─ index.js| | ├─ pageB // pageB的router,应用了history模式| | ├─ index.js| ├─ store| | ├─ pageA // pageA的Vuex| | ├─ index.js| | ├─ pageB // pageB的Vuex| | ├─ index.js| ├─ views| | ├─ pageA // pageA的页面,写法和之前一个的单页利用统一| | ├─ main.js // 注入了mode,挂载到了vue的原型上,应用this能够获取环境变量| | ├─ pageA.vue| | ├─ pageB // pageB的页面,写法和之前一个的单页利用统一| | ├─ main.js // 注入了mode,挂载到了vue的原型上,应用this能够获取环境变量| | ├─ pageB.vue├─ scripts ├─ babel.config.js // 配置es转化语法├─ vue.config.js // vue cli打包相干配置├─ app.json // 寄存各个多页利用的public、router、vuex、views入口地址实际配置 ...

February 27, 2023 · 5 min · jiezi

关于vue.js:Nuxt3实战系列之配置管理

Nuxt提供了一个运行时配置API,在你的应用程序和服务器路由中裸露配置,并能在运行时通过设置环境变量进行更新。 定义运行时配置为了将配置和环境变量裸露给应用程序,你须要在nuxt.config文件中应用runtimeConfig选项定义运行时配置。 export default defineNuxtConfig({ runtimeConfig: { // 只在服务端能够拜访的配置项 apiSecret: '123', // 能够裸露给客户端应用的配置项 public: { apiBase: '/api' } }})须要注意的一点是,默认定义的配置选项都只能在服务端流程中可拜访,如果须要在客户端也能应用,须要将配置项定义在public选项内。配置项定义好后,咱们能够应用useRuntimeConfigApi去获取定义的配置项的值。 <script setup> const runtimeConfig = useRuntimeConfig() console.log(runtimeConfig.apiSecret) console.log(runtimeConfig.public.apiBase)</script><template> <div> <NuxtWelcome /> </div></template>应用 Options API 时,咱们也可通过 this.$config.public 获取到运行时配置。配置好后,从新运行一下程序,咱们能够在终端控制台以及浏览器的控制台看到打印的配置项。终端中显示的是服务端执行时候打印的配置,能够看到都胜利的获取到了值。浏览器管制台上打印的是在客户端上执行的后果,能够看到,apiSecret的值为undefined,的确是没有获取到。 序列化运行时配置在传递给Nitro之前会被序列化。这意味着,任何不能被序列化而后反序列化的货色(如Function、Sets, Maps等),都不应该在nuxt.config中设置。你能够把这些代码放在Nuxt或Nitro的插件或中间件中,而不是从nuxt.config中把不可序列化的对象或函数传入你的应用程序。 环境变量提供配置的最常见办法是应用环境变量。Nuxi CLI 内置反对在开发、构建和生成过程中读取 .env 文件。然而当你运行你构建的服务器时,你的 .env 文件将不会被读取。运行时配置值在运行时主动替换为匹配的环境变量,但有两个要害要求: 您须要的变量必须在您的 nuxt.config 中定义。这可确保任意环境变量不会裸露给您的利用程序代码。只有特地命名的环境变量能力笼罩运行时配置属性。规定是用NUXT_ 结尾的大写环境变量,应用 _ 来分隔键和大小写变动。示例如下: NUXT_API_SECRET=api_secret_tokenNUXT_PUBLIC_API_BASE=https://nuxtjs.orgexport default defineNuxtConfig({ runtimeConfig: { apiSecret: '', // 该项会被 NUXT_API_SECRET 环境变量笼罩 public: { apiBase: '', // 该项会被 NUXT_PUBLIC_API_BASE 环境变量笼罩 } },})增加完环境变量后,须要从新运行下我的项目能力失效,从新运行后,再看打印的值。能够发现环境变量获取胜利了。 ...

February 24, 2023 · 1 min · jiezi

关于vue.js:Echarts-自定义-label标签-formatter的样式

如图所示,我须要自定义价格千分位显示,以及字体款式调整依据官网文档,能够通过formatter函数自定义返回内容。通过|自定义属性名字联合rich调整每个字符的款式。 间接上代码 option1: { tooltip: { trigger: 'item', formatter: '{a} <br/>{b} : ¥{c} ({d}%)' }, series: [ { name: '费用占比', type: 'pie', radius: [50, 140], roseType: 'area', itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 }, label: { formatter (params) { return `{name|${params.name}}\n¥{value|${(+params.value).toLocaleString('en-US')}} {percent|${params.percent}}%` }, rich: { name: { color: '#86909C', fontSize: 12, lineHeight: 26 }, value: { color: '#1D2129', fontSize: 14, lineHeight: 22 }, percent: { color: '#1D2129', fontSize: 14, lineHeight: 22 } } }, data: [ { value: 10000, name: 'rose 2' }, { value: 5000, name: 'rose 2' }, { value: 2000, name: 'rose 3' }, { value: 1000, name: 'rose 5' }, { value: 1000, name: 'rose 6' } ] } ] }

February 24, 2023 · 1 min · jiezi

关于vue.js:React-Vue-从vue切换到react-组件的封装和使用

在组件封装上,两个框架差的不远。 在Vue中封装组件,用插槽的形式子组件中写好具名插槽的地位和款式,在父组件中通过插槽名传入html和js逻辑 局部。 在react中封装组件,区别于Vue的中央在于,Vue中有插槽这个语法,而react中没有。在react中的html和js逻辑局部的传递都须要借由属性组件的属性(Props)来实现。 (在Vue中插槽传值,在react中属性传值) 此外,Vue中组件的款式(css局部) 能够间接写在组件中(写在<style>标签中),而react须要独自开一份样式表。 当初要实现下图这样的一个Card组件,预留左上角、右上角、内容区等几个地位(这些地位能够传入html) 在vue中的实现 (CardWithSlot.vue 仅template局部,省略款式) 应用: 代码见:https://github.com/DiracKeeko... 在react中的实现 (CardWithSlot.tsx 仅tsx局部,省略样式表) 应用: 代码见:https://github.com/DiracKeeko... 完结。 同步更新到本人的语雀https://www.yuque.com/diracke...

February 24, 2023 · 1 min · jiezi

关于vue.js:vue面试题

1、MVVM模型 MVVM,是Model-View-ViewModel的简写,其本质是MVC模型的升级版。其中 Model 代表数据模型,View 代表看到的页面,ViewModel是View和Model之间的桥梁,数据会绑定到ViewModel层并主动将数据渲染到页面中,视图变动的时候会告诉ViewModel层更新数据。以前是通过操作DOM来更新视图,当初是数据驱动视图。2、Vue的生命周期Vue 的生命周期能够分为8个阶段:创立前后、挂载前后、更新前后、销毁前后,以及一些非凡场景的生命周期。Vue 3 中还新增了是3个用于调试和服务端渲染的场景。 Vue 2中的生命周期钩子Vue 3选项式API的生命周期选项Vue 3 组合API中生命周期钩子形容beforeCreatebeforeCreatesetup()创立前,此时data和 methods的数据都还没有初始化createdcreatedsetup()创立后,data中有值,尚未挂载,能够进行一些Ajax申请beforeMountbeforeMountonBeforeMount挂载前,会找到虚构DOM,编译成RendermountedmountedonMounted挂载后,DOM已创立,可用于获取拜访数据和DOM元素beforeUpdatebeforeUpdateonBeforeUpdate更新前,可用于获取更新前各种状态updatedupdatedonUpdated更新后,所有状态已是最新beforeDestroybeforeUnmountonBeforeUnmount销毁前,可用于一些定时器或订阅的勾销destroyedunmountedonUnmounted销毁后,可用于一些定时器或订阅的勾销activatedactivatedonActivatedkeep-alive缓存的组件激活时deactivateddeactivatedonDeactivatedkeep-alive缓存的组件停用时errorCapturederrorCapturedonErrorCaptured捕捉一个来自子孙组件的谬误时调用—renderTrackedonRenderTracked调试钩子,响应式依赖被收集时调用—renderTriggeredonRenderTriggered调试钩子,响应式依赖被触发时调用—serverPrefetchonServerPrefetch组件实例在服务器上被渲染前调用3、父子组件的生命周期 加载渲染阶段:父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted更新阶段:父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated销毁阶段:父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed4、Vue.$nextTick 在下次 DOM 更新循环完结之后执行提早回调。在批改数据之后立刻应用这个办法,获取更新后的 DOM。 nextTick 是 Vue 提供的一个全局 API,因为 Vue 的异步更新策略,导致咱们对数据批改后不会间接体现在 DOM 上,此时如果想要立刻获取更新后的 DOM 状态,就须要借助该办法。Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue 将开启一个异步更新队列,并缓冲在同一事件循环中产生的所有数据变更。如果同一个 watcher 被屡次触发,只会被推入队列一次。这种在缓冲时去除反复数据对于防止不必要的计算和 DOM 操作是十分重要的。nextTick办法会在队列中退出一个回调函数,确保该函数在后面的 DOM 操作实现后才调用。应用场景:1、如果想要在批改数据后立即失去更新后的DOM构造,能够应用Vue.nextTick()2、在created生命周期中进行DOM操作5、Vue 实例挂载过程中产生了什么 ...

February 24, 2023 · 3 min · jiezi

关于vue.js:Elementui日期区间组件动态禁用日期disabledDate

需要,默认抉择十天区间,并且每次抉择的日期必须小余90天。 /** * 日期控件动静设置最大跨度,解决elementui日期区间,第一次点击日期,动静解决disabledDate禁用日期的跨度。 * @param {Number} _this this * @param {Number} day 天 * @returns {Blob} 返回pickerOptions数据 * @example this.$fc.pickerOptionsDynamic(this, 90); * */export function pickerOptionsDynamic(_this, day) { const time = 3600*1000*24*day; let minDate = null; const pickerOptions = { disabledDate(time) { if (minDate === null) { const min = Date.now() - time; return time.getTime() > Date.now() || time.getTime() < min; } else { const max = new Date(minDate).getTime() + time; const min = new Date(minDate).getTime() - time; if (max >= Date.now()) { return time.getTime() > Date.now() || time.getTime() < min; } else { return time.getTime() > max || time.getTime() < min; } } }, onPick: (opt) => { minDate = _this.parseTime(opt.minDate, '{y}-{m}-{d}'); } } return pickerOptions;}<el-date-picker v-model="queryParams.date" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="完结日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd" :clearable="false" :editable="false" :unlink-panels="true" :picker-options="$yFc.pickerOptionsDynamic(this, 90)"></el-date-picker>

February 22, 2023 · 1 min · jiezi

关于vue.js:Vue2-和-Vue3-性能比较小实验

别离应用 Vue2 和 Vue3 创立一个组件,用一个对象数组作为组件的状态,以它的长度作为变量,考查 Vue2 和 Vue3 性能。 内存占用数组长度Vue2Vue3110.2 MB11.1 MB10 00017.9 MB12.1 MB100 00067.4 MB14.4 MB1 000 000568 MB36.0 MB初始化工夫数组长度Vue2Vue317.2 ms7.8 ms10 000110 ms6.9 ms100 000803 ms6.7 ms1 000 0002282 ms7.0 ms测试代码Vue2<!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"> <title>Document</title> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script></head><body> <div id="app">{{ message }}</div> <script> const len = 1000000 const arr = new Array(len) for (let i = 0; i < len; i++) { arr[i] = { id: i, name: 'test' } } console.time('vue2:') new Vue({ el: '#app', data() { return { message: 'Hello Vue!', arr } }, created() { console.timeEnd('vue2:') } }) </script></body></html>Vue3<!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"> <title>Document</title> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script></head><body> <div id="app">{{ message }}</div> <script> const { createApp } = Vue const len = 1000000 const arr = new Array(len) for (let i = 0; i < len; i++) { arr[i] = { id: i, name: 'test' } } console.time('vue3:') createApp({ data() { return { message: 'Hello Vue!', arr } }, created() { console.timeEnd('vue3:') } }).mount('#app') </script></body></html>揣测Vue2 应用Object.defineProperty()创立响应式属性,初始化时,除了遍历props和data的每个属性,还会深度遍历子对象和子数组,从新定义它们的属性,并创立如Observer和Dep实例,来察看属性变动,并且Object.defineProperty()重写的get和set办法也是挂载在实例上。深度遍历加上创立这些实例的花销远大于原始对象,于是,能够看到 Vue2 无论是内存,还是初始化工夫随着数组长度减少猛涨,和 Vue3 产生了显著的差距。 ...

February 21, 2023 · 2 min · jiezi

关于vue.js:atreeselect-和-aselect的区别及使用

实现select的复选、全选、清空、搜寻等性能<template> <a-spin :spinning="loading" tip="申请中..."> <div class="workCurve"> <x-card> <div slot="content" class="table-page-search-wrapper"> <a-form layout="inline"> <span class="search-form-left" style="width:100%"> <a-row :gutter="0"> <a-col :md="4" :sm="24"> <a-form-item label=""> <a-radio-group v-model="type" style="width:230px" @change="typeChange"> <a-radio value="0"> 投产日期 </a-radio> <a-radio value="1"> 措施日期 </a-radio> </a-radio-group> </a-form-item> </a-col> <a-col :md="4" :sm="12" style="margin-left:20px"> <a-form-item label="拉齐数据"> <a-select :maxTagCount="1" mode="multiple" style="width: 100%;min-width:240px" placeholder="请抉择拉齐数据" v-model="lqData" > <a-select-option v-for="(item, index) in lqOptions" :key="index" :value="item.value">{{ item.name }}</a-select-option> </a-select> </a-form-item> </a-col> <a-col :md="4" :sm="24"> <a-form-item label="向前拉齐点数"> <a-input-number :disabled="type === '0'" v-model="beforeCount" :precision="0" style="width: 100%;" /> </a-form-item> </a-col> <a-col :md="4" :sm="24"> <a-form-item label="向前每段天数"> <a-input-number :disabled="type === '0'" v-model="spanLenth" :precision="0" style="width: 100%;" /> </a-form-item> </a-col> <a-col :md="4" :sm="24"> <a-form-item label="向后拉齐点数"> <a-input-number v-model="afterCount" :precision="0" style="width: 100%;" /> </a-form-item> </a-col> <a-col :md="4" :sm="24"> <a-form-item label="向后每段天数"> <a-input-number v-model="afterDaily" :precision="0" style="width: 100%;" /> </a-form-item> </a-col> <a-col :md="4" :sm="24"> <a-form-item> <a-button type="primary" @click="handleQuery" ><img style="width:12px;margin-right:4px" src="@/assets/search-icon.png" alt="" />查问</a-button > </a-form-item> </a-col> <span class="table-page-search-submitButtons button-group-right"> <a-button type="primary" @click="visible = true">查看数据表</a-button> </span> </a-row> </span> </a-form> </div> </x-card> <div> <a-row :gutter="[5, 5]" type="flex"> <a-col :span="4" flex="240px"> <QkTree ref="qkTree" :type="'well'" :disabledJh="type === '0' ? true : false" :multiple="false" :treeStyle="'height:calc(100vh - 227px)'" :checkable="false" @selectedNode="selectedNodeHandle" ></QkTree> </a-col> <a-col :span="15" flex="calc(100% - 240px - 340px - 10px)" style="width:calc(100% - 240px - 340px - 10px)"> <a-card class="charts-contaniner"> <div class="charts-box"> <curve-chart ref="curveChart"></curve-chart> </div> </a-card> </a-col> <a-col :span="5" flex="340px"> <a-card class="charts-contaniner"> <div style="height: calc(100vh - 395px);overflow: auto;"> <a-tree-select v-if="false" v-model="rightJh" :maxTagCount="1" @change="rightJhChange" :dropdownStyle="{ maxHeight: '300px', overflow: 'auto' }" style="width:100%;margin-bottom:10px" :tree-data="rightTreeData" tree-checkable allowClear :treeDefaultExpandAll="true" :replaceFields="{ children: 'children', title: 'jh', key: 'jh', value: 'jh' }" placeholder="请输出井号前缀并抉择" > </a-tree-select> <a-select style="width:100%;margin-bottom:10px" class="select" mode="multiple" v-model="rightJh" :maxTagCount="1" placeholder="请输出井号前缀并抉择" @change="rightJhChange" > <div slot="dropdownRender" slot-scope="menu"> <div style="padding:4px; cursor: pointer;" @mousedown="e => e.preventDefault()"> <!-- 全选、重置 --> {{ checkAllLoading }} <a-button class="btn" type="primary" style="margin-right:10px" @click="checkAllField" :loading="checkAllLoading" > 全选 </a-button> <a-button class="btn" @click="resetRightJh">重置</a-button> </div> <a-divider style="margin: 4px 0;" /> <VNodes :vnodes="menu" /> </div> <a-select-option v-for="item in dataImport" :key="item.jh" :value="item.jh"> {{ item.jh }} </a-select-option> </a-select> <!-- :show-checked-strategy="SHOW_PARENT" --> <a-table :loading="loadingImport" :scroll="{ y: 'calc(100vh - 495px)' }" :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange, fixed: true, columnWidth: 50 }" :columns="columnsImport" :data-source="rightTableData" bordered size="small" :pagination="false" :rowKey="(record, index) => record.jh" > <span slot="rqStr" slot-scope="text, record"> <a-input :placeholder="''" @change="e => handleChange(e, record.rqStr, 'rqStr')" v-model="record.rqStr" > </a-input> </span> </a-table> </div> <div style="width: 100%;text-align: center;"> <a-button style="margin-top: 15px;" type="primary" @click="handleDownload">模板下载</a-button><br /> <a-upload name="file" :before-upload="beforeUpload" :customRequest="handleUpload" accept=".xls,.xlsx" :showUploadList="false" > <a-button type="primary" style="margin-top: 15px;"> <a-icon type="import" />导入 </a-button> </a-upload> <br /> <a-button style="margin-top: 15px;" type="primary" @click="handleClear">清空</a-button> </div> </a-card> </a-col> </a-row> </div> <a-modal :footer="null" v-model="visible" :width="1200" title="拉齐比照数据" :destroyOnClose="true" @cancel="cancelTableData" @ok="setTableData" > <a-table :columns="columns" size="small" :data-source="data" bordered> <div v-for="col in columns.map(it => it.dataIndex)" :key="col" :slot="col" slot-scope="text, record"> <div> <a-input-number :min="-999999" :max="999999" v-if="record.editable" style="margin: -5px 0;width:100%" :value="text" @change="e => handleChange(e, record.key, col)" /> <template v-else> {{ text }} </template> </div> </div> <template slot="operation" slot-scope="text, record"> <div class="editable-row-operations"> <span v-if="record.editable"> <a @click="() => save(record.key)">保留</a> <a @click="() => cancel(record.key)" style="margin-left:15px">勾销</a> </span> <span v-else> <a :disabled="editingKey !== ''" @click="() => edit(record.key)">编辑</a> </span> </div> </template> </a-table> </a-modal> </div> </a-spin></template><script>import { XCard } from '@/components'import { handleXlsRes } from '@/utils/exportXls.js'import CurveChart from '../components/CurveChart.vue'import QkTree from '@/components/QkTree/index.vue'import _ from 'lodash'import { curveApi, importApi, getTcrqJhListApi, getImportTemplateApi} from '@/api/modular/main/prodIndicator/compair.js'export default { components: { XCard, QkTree, CurveChart, VNodes: { functional: true, render: (h, ctx) => ctx.props.vnodes } }, data() { return { checkAllLoading: false, selectedRowKeys: [], selectedRows: [], lqOptions: [ // 总井数、开井数、日产液量、日产油量、含水、动液面、阶段累计产液、阶段累计产油 { name: '总井数', value: 'zjs' }, { name: '开井数', value: 'kjs' }, { name: '日产液量', value: 'rcyel' }, { name: '日产油量', value: 'rcyl' }, { name: '含水', value: 'hsb' }, { name: '动液面', value: 'dym' }, { name: '阶段累计产液', value: 'ljcyel' }, { name: '阶段累计产油', value: 'ljcyl' } ], lqData: ['zjs', 'kjs', 'rcyel', 'rcyl', 'hsb', 'dym', 'ljcyel', 'ljcyl'], loading: false, mode: ['month', 'month'], month: [], queryParam: {}, columns: [ { title: '阶段号', dataIndex: 'stage', scopedSlots: { customRender: 'stage' } }, { title: '总井数', dataIndex: 'zjs', scopedSlots: { customRender: 'zjs' } }, { title: '开井数', dataIndex: 'kjs', scopedSlots: { customRender: 'kjs' } }, { title: '日产液量', dataIndex: 'rcyel', scopedSlots: { customRender: 'rcyel' } }, { title: '日产油量', dataIndex: 'rcyl', scopedSlots: { customRender: 'rcyl' } }, { title: '动液面(m)', dataIndex: 'dym', scopedSlots: { customRender: 'dym' } }, { title: '含水(%)', dataIndex: 'hsb', scopedSlots: { customRender: 'hsb' } }, { title: '阶段累计产液', dataIndex: 'ljcyel', scopedSlots: { customRender: 'ljcyel' } }, { title: '阶段累计产油', dataIndex: 'ljcyl', scopedSlots: { customRender: 'ljcyl' } } ], wellOptions: [], visible: false, editingKey: '', data: [], cacheData: [], renderData: { xdata: [], data: [] }, chartsYName: [ '总井\n数(口)', '开井\n数(口)', '日产\n液(t)', '日产\n油(t)', '含水\n(%)', '动液\n面(m)', '阶段累计产液', '阶段累计产油' ], //'开井\n数(口)', chartsName: [ { name: '总井数', index: 0, color: '#4b5cc4', type: 'line', key: 'zjs' }, { name: '开井数', index: 1, color: '#0c8918', type: 'line', key: 'kjs' }, { name: '日产液', index: 2, color: '#ff2aff', type: 'line', key: 'rcyel' }, { name: '日产油', index: 3, color: 'red', type: 'line', key: 'rcyl' }, { name: '含水', index: 4, color: '#6fff6f', type: 'line', key: 'hsb' }, { name: '动液面', index: 5, color: '#ffe16b', type: 'line', key: 'dym' }, { name: '阶段累计产液', index: 6, color: '#f05654', type: 'line', key: 'ljcyel' }, { name: '阶段累计产油', index: 7, color: '#c9dd22', type: 'line', key: 'ljcyl' } ], miningStatusData: {}, devHistory: '', maxmonth: 100, tabPosition: 'jzzjg', sliderValue: [0, 0], btnLabel: '锁定距离', isLock: false, intervalDate: 0, monthDate: [], columnsImport: [ { title: '井号', dataIndex: 'jh', align: 'center' }, { title: '日期', dataIndex: 'rqStr', align: 'center', width: 140, scopedSlots: { customRender: 'rqStr' } } ], tcJhList: [], rightJh: [], rightTableData: [], dataImport: [], spanLenth: 1, beforeCount: 1, afterCount: 1, afterDaily: 1, type: '0', loadingImport: false, selectedQk: {}, selectedCw: {} } }, computed: { jhList() { if (this.selectedQk.title && this.selectedQk.qkdm) { if (this.type === '1') { this.loadingImport = true //措施日期,jh取值为左侧树;投产日期,井号list数据为接口返回 let jhArr = _.map(this.selectedQk.children, 'jh') let tableData = [] jhArr.filter(item => { if (!!item) { tableData.push({ jh: item }) } return !!item }) this.loadingImport = false return tableData } } else { if (this.type === '1') { this.$message.warning('请抉择区块') } } return [] }, queryData() { return { beforeCount: this.beforeCount, beforeDaily: this.beforeDaily, afterCount: this.afterCount, afterDaily: this.afterDaily, wellList: this.selectedRows } }, selectedQkInfo() { let arr = [this.selectedQk] return { zyqdm: arr && arr[0] ? arr[0].zyqdm : null, qkdm: arr && arr[0] ? arr[0].qkdm : null, cyddm: arr && arr[0] ? arr[0].cyddm : null } }, rightTreeData() { let arr = [] if (this.dataImport?.length) { arr = [{ jh: '全选', children: this.dataImport }] } return arr } }, methods: { resetRightJh() { this.rightJh = [] this.rightJhChange(this.rightJh) }, checkAllField() { this.checkAllLoading = true let jhArr = [] this.dataImport.forEach(item => { jhArr.push(item.jh) }) this.rightJh = jhArr this.rightJhChange(this.rightJh) }, rightJhChange(val) { console.log(this.checkAllLoading) // console.log(22, val) let arr = [] let arr2 = [] this.dataImport.forEach(iitem => { let obj = val.find(valItem => { return iitem.jh === valItem }) if (!obj) { arr.push(iitem) } else { arr2.push(iitem) } }) this.rightTableData = arr2.concat([]) this.defaultCheckedAll(arr2) //选中以后下拉井号 if (this.checkAllLoading) { // setTimeout(() => { // this.checkAllLoading = false // }, 1000) } }, filterOption(input, option) { return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 }, typeChange(val) { this.dataImport = [] // 类型变更,表格选中项重置 this.selectedRowKeys = [] this.selectedRows = [] if (this.type === '0') { this.beforeCount = null this.beforeDaily = null this.getTcJhList() //切换时,从新申请 } else { this.beforeCount = 1 this.beforeDaily = 1 this.dataImport = this.jhList this.rightTableData = [] this.defaultCheckedAll([]) } // 重置选中数据 this.rightTableData = [] this.defaultCheckedAll([]) // 重置曲线 this.setCurveData([]) this.data = [] }, onSelectChange(selectedRowKeys, selectedRows) { this.selectedRowKeys = selectedRowKeys this.selectedRows = selectedRows this.rightJh = this.selectedRowKeys //表格抉择内容变更同步批改a-tree-select项 }, getTcJhList() { if (!this.selectedQk.title) { this.tcJhList = [] this.dataImport = [] this.defaultCheckedAll([]) return } // 获取投产日期下所有井号 let params = Object.assign({}, this.queryData, this.selectedQkInfo) // 点击 作业区 和采油队时 提醒用户点击 请抉择区块。 if (!params.qkdm) { this.$message.warning('请抉择区块') return } this.loadingImport = true getTcrqJhListApi(params) .then(res => { this.loadingImport = false if (res.data) { this.tcJhList = res.data this.rightJh = [] this.dataImport = this.tcJhList this.rightTableData = [] this.defaultCheckedAll([]) } else { this.$message.warning(res.message) } }) .catch(() => { this.loadingImport = false }) }, defaultCheckedAll(arr) { let indArr = [] arr.forEach(item => { indArr.push(item.jh) }) // 默认选中全副 this.onSelectChange(indArr, arr) }, selectedNodeHandle(value) { this.selectedQk = value if (this.type === '0') { this.getTcJhList() } else { this.dataImport = this.jhList this.rightTableData = [] this.defaultCheckedAll([]) } }, // 用表格编辑后的数据从新绘制曲线 setTableData() { this.visible = false this.setCurveData(this.data) }, cancelTableData() { this.visible = false this.cancel(this.editingKey) }, handleChange(value, key, column) { const newData = [...this.dataImport] const target = newData.find(item => key === item.key) if (target) { target[column] = value this.dataImport = newData } }, edit(key) { const newData = [...this.data] const target = newData.find(item => key === item.key) this.editingKey = key if (target) { target.editable = true this.data = newData } }, save(key) { const newData = [...this.data] const newCacheData = [...this.cacheData] const target = newData.find(item => key === item.key) const targetCache = newCacheData.find(item => key === item.key) if (target && targetCache) { delete target.editable this.data = newData Object.assign(targetCache, target) this.cacheData = newCacheData } this.editingKey = '' }, cancel(key) { const newData = [...this.data] const target = newData.find(item => key === item.key) this.editingKey = '' if (target) { Object.assign( target, this.cacheData.find(item => key === item.key) ) delete target.editable this.data = newData } }, getCurveData() { let params = Object.assign({}, this.queryData, {}) if (this.type === '0') { params.beforeCount = null params.beforeDaily = null // 向前拉齐点数:禁用,并且不传参置为null // 向前拉齐每段天数:禁用,并且不传参置为null } let _this = this _this.loading = true curveApi(params) .then(res => { if (res.code != 200) { this.$message.warning(res.message) _this.setCurveData([]) _this.data = [] return } if (res.success && res.data.length > 0) { _this.data = res.data for (var m = 0; m < _this.data.length; m++) { _this.data[m]['key'] = m.toString() } _this.cacheData = _this.data.map(item => ({ ...item })) _this.miningStatusData = res.data[0] _this.setCurveData(res.data) } else { _this.setCurveData([]) _this.data = [] } _this.loading = false }) .finally(err => { _this.loading = false }) }, setCurveData(data) { let chartsNameData = [] this.renderData = { xdata: [], data: [], chartsYName: [] } let showCurveData = this.lqData let chartsNameShow = [] let chartsYNameShow = [] showCurveData.forEach((currentItem, index) => { let obj = this.chartsName.find(item => { return item.key === currentItem }) if (obj) { obj.index = index let name = obj.name.length > 3 ? obj.name.slice(0, 3) + '\n' + obj.name.slice(3) : obj.name chartsYNameShow.push(name) chartsNameShow.push(obj) } }) chartsNameData = chartsNameShow this.renderData.chartsYName = chartsYNameShow || this.chartsYName for (var i = 0; i < data.length; i++) { this.renderData.xdata.push('阶段' + data[i].stage) } for (var n = 0; n < chartsNameData.length; n++) { let obj = { name: chartsNameData[n].name, index: chartsNameData[n].index, color: chartsNameData[n].color, type: chartsNameData[n].type, data: [] } for (var i = 0; i < data.length; i++) { let key = chartsNameData[n].key obj.data.push(data[i][key] ? data[i][key] - 0 : 0) } this.renderData.data.push(obj) } this.$refs.curveChart.setData(this.renderData) }, handleQuery() { if (!this.afterCount) { return this.$message.warning('请输出向后拉齐点数') } if (!this.afterDaily) { return this.$message.warning('请输出向后每段天数') } if (!this.selectedRows.length) { return this.$message.warning('井号不能为空') } let flag = true if (this.selectedRows) { this.selectedRows.forEach(item => { if (!item.rqStr) { flag = false } }) } if (!flag) { this.$message.warning('所选井号日期不能为空') return } this.getCurveData() }, // 模板下载 handleDownload() { getImportTemplateApi().then(res => { // handleXlsRes(res) const dataJson = res.data const aLink = document.createElement('a') const blob = new Blob([dataJson], { type: 'application/force-download; charset=UTF-8' }) aLink.href = URL.createObjectURL(blob) aLink.setAttribute('download', '拉齐比照模板.xls') // 设置下载文件名称 aLink.click() }) }, // 导入 beforeUpload(file) { const extension = file.name.substring(file.name.lastIndexOf('.') + 1) if (extension !== 'xlsx' && extension !== 'xls') { this.$message.error('只能上传excel文件') return false } const isLt10M = file.size / 1024 / 1024 < 10 if (!isLt10M) { this.$message.error('大小不能超过10MB!') return false } return extension && isLt10M }, handleUpload(file) { const _this = this const formData = new FormData() formData.append('file', file.file) importApi(formData) .then(response => { if (Object.prototype.toString.call(response.data) === '[object Blob]') { // 先判断是不是blob格局 if (response.data.type.includes('application/json')) { // 判断是不是非凡状况(json) const reader = new FileReader() reader.onload = function() { const res = reader.result const result = JSON.parse(res) // blob格局转成json if (result.code === 200) { // 导出超出限度 _this.$message.success('导入胜利') _this.dataImport = result.data _this.rightTableData = [] _this.defaultCheckedAll([]) } else { _this.$message.error(result.message) } } reader.readAsText(response.data) } else { // 导入格局谬误 handleXlsRes(response) } } }) .catch(e => { // 接管异样 并提醒 _this.$message.error(e.message) }) }, handleClear() { this.dataImport = [] this.rightTableData = [] this.defaultCheckedAll([]) } }, created() {}}</script><style lang="less" scoped>.charts-contaniner { overflow: auto; ul, li { margin: 0; padding: 0; list-style-type: none; } .charts-box { width: 100%; height: calc(100vh - 253px); overflow: hidden; }}.cardContent { min-height: 200px; p { margin-bottom: 6px; color: #1890ff; }}.workCurve { /deep/ .ant-card-body { padding: 12px; } /deep/ .ant-form-item { margin-bottom: 6px; } /deep/ .ant-modal-body { padding: 12px; }}.btnBoxRight { padding-top: 12px; text-align: right; button { margin: 0; }}.bottomTabs { background-color: #fff; padding-top: 8px; /deep/ .ant-radio-button-wrapper { width: 50%; text-align: center; }}/deep/.ant-table-small > .ant-table-content > .ant-table-body { margin: 0 !important;}</style>

February 21, 2023 · 10 min · jiezi

关于vue.js:ElementUI-Select-组件的-BUG

ElementUI Select 组件的 BUG概要本文用于记录 ElementUI 中 Select 组件的一个缺点,并提供了一个解决该缺点的计划(不魔改源码)。 该缺点只有在特定的场景下才能够复现,不影响大多数场景的应用;复现版本:所有版本。适配 Vue 3.x 的 Element-Plus 没有该问题。问题景象先通过这张动图感受一下: 能够看到,Select的值3与选项中的333对应不上。上述概要中提到的非凡场景就是:选项是异步加载的,在这之前先关上了下拉框。 代码:<div id="app"> <el-select v-model="selectValue" placeholder="请抉择"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> </el-select></div>new Vue({ el: '#app', data: { options: [] }, computed: { selectValue() { const initialValue = 3 const found = this.options.find((line) => line.value === initialValue); return found ? found.value : ''; }, }, created() { setTimeout(() => { this.options = [ { value: 1, label: '111', }, { value: 2, label: '222', }, { value: 3, label: '333', }, { value: 4, label: '444', }, ]; }, 1500); }})理论冀望咱们的冀望是:在没有选项时,不显示 Select 的值,并且在选项加载胜利后正确显示对应的选项。 ...

February 21, 2023 · 1 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 更新操作 ...

February 21, 2023 · 2 min · jiezi

关于vue.js:高级前端二面必会vue面试题合集

应用 Object.defineProperty() 来进行数据劫持有什么毛病?在对一些属性进行操作时,应用这种办法无奈拦挡,比方通过下标形式批改数组数据或者给对象新增属性,这都不能触发组件的从新渲染,因为 Object.defineProperty 不能拦挡到这些操作。更准确的来说,对于数组而言,大部分操作都是拦挡不到的,只是 Vue 外部通过重写函数的形式解决了这个问题。 在 Vue3.0 中曾经不应用这种形式了,而是通过应用 Proxy 对对象进行代理,从而实现数据劫持。应用Proxy 的益处是它能够完满的监听到任何形式的数据扭转,惟一的毛病是兼容性的问题,因为 Proxy 是 ES6 的语法。 Vue 修饰符有哪些事件修饰符 .stop 阻止事件持续流传.prevent 阻止标签默认行为.capture 应用事件捕捉模式,即元素本身触发的事件先在此处解决,而后才交由外部元素进行解决.self 只当在 event.target 是以后元素本身时触发处理函数.once 事件将只会触发一次.passive 通知浏览器你不想阻止事件的默认行为v-model 的修饰符 .lazy 通过这个修饰符,转变为在 change 事件再同步.number 主动将用户的输出值转化为数值类型.trim 主动过滤用户输出的首尾空格键盘事件的修饰符 .enter.tab.delete (捕捉“删除”和“退格”键).esc.space.up.down.left.right零碎润饰键 .ctrl.alt.shift.meta鼠标按钮修饰符 .left.right.middledelete和Vue.delete删除数组的区别delete 只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变。Vue.delete 间接删除了数组 扭转了数组的键值。Vue complier 实现模板解析这种事,实质是将数据转化为一段 html ,最开始呈现在后端,通过各种解决吐给前端。随着各种 mv* 的衰亡,模板解析交由前端解决。总的来说,Vue complier 是将 template 转化成一个 render 字符串。能够简略了解成以下步骤:parse 过程,将 template 利用正则转化成AST 形象语法树。optimize 过程,标记动态节点,后 diff 过程跳过动态节点,晋升性能。generate 过程,生成 render 字符串Vue.js的template编译简而言之,就是先转化成AST树,再失去的render函数返回VNode(Vue的虚构DOM节点),具体步骤如下: 首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的形象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创立编译器的。另外compile还负责合并option。 ...

February 21, 2023 · 5 min · jiezi

关于vue.js:社招前端一面必会vue面试题

DIFF算法的原理在新老虚构DOM比照时: 首先,比照节点自身,判断是否为同一节点,如果不为雷同节点,则删除该节点从新创立节点进行替换如果为雷同节点,进行patchVnode,判断如何对该节点的子节点进行解决,先判断一方有子节点一方没有子节点的状况(如果新的children没有子节点,将旧的子节点移除)比拟如果都有子节点,则进行updateChildren,判断如何对这些新老节点的子节点进行操作(diff外围)。匹配时,找到雷同的子节点,递归比拟子节点在diff中,只对同层的子节点进行比拟,放弃跨级的节点比拟,使得工夫简单从O(n3)升高值O(n),也就是说,只有当新旧children都为多个子节点时才须要用外围的Diff算法进行同层级比拟。 vuex是什么?怎么应用?哪种性能场景应用它?Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源寄存地,对应于个别 vue 对象外面的 data 外面寄存的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据产生扭转,依赖这相数据的组件也会产生更新它通过 mapState 把全局的 state 和 getters 映射到以后组件的 computed 计算属性vuex 个别用于中大型 web 单页利用中对利用的状态进行治理,对于一些组件间关系较为简单的小型利用,应用 vuex 的必要性不是很大,因为齐全能够用组件 prop 属性或者事件来实现父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据。应用Vuex解决非父子组件之间通信问题 vuex 是通过将 state 作为数据中心、各个组件共享 state 实现跨组件通信的,此时的数据齐全独立于组件,因而将组件间共享的数据置于 State 中能无效解决多层级组件嵌套的跨组件通信问题vuex 的 State 在单页利用的开发中自身具备一个“数据库”的作用,能够将组件中用到的数据存储在 State 中,并在 Action 中封装数据读写的逻辑。这时候存在一个问题,个别什么样的数据会放在 State 中呢? 目前次要有两种数据会应用 vuex 进行治理:组件之间全局共享的数据通过后端异步申请的数据 包含以下几个模块 state:Vuex 应用繁多状态树,即每个利用将仅仅蕴含一个store 实例。外面寄存的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据产生扭转,依赖这相数据的组件也会产生更新。它通过 mapState 把全局的 state 和 getters 映射到以后组件的 computed 计算属性mutations:更改Vuex的store中的状态的惟一办法是提交mutationgetters:getter 能够对 state 进行计算操作,它就是 store 的计算属性尽管在组件内也能够做计算属性,然而 getters 能够在多给件之间复用如果一个状态只在一个组件内应用,是能够不必 gettersaction:action 相似于 muation, 不同在于:action 提交的是 mutation,而不是间接变更状态action 能够蕴含任意异步操作modules:面对简单的应用程序,当治理的状态比拟多时;咱们须要将vuex的store对象宰割成模块(modules) ...

February 21, 2023 · 12 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监听对象属性的变动,那么问题来了,咱们常常在传递数据的时候往往不是一个对象,很有可能是一个数组,那是不是就没有方法了呢,答案显然是否则的。那么上面就看看作者是如何监听数组的变动: ...

February 21, 2023 · 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>这样就只判断一次就能达到渲染成果了,是不是更好一些那参考 前端进阶面试题具体解答 ...

February 20, 2023 · 3 min · jiezi

关于vue.js:美团前端必会vue面试题合集

delete和Vue.delete删除数组的区别delete 只是被删除的元素变成了 empty/undefined 其余的元素的键值还是不变。Vue.delete 间接删除了数组 扭转了数组的键值。Vue3有理解过吗?能说说跟vue2的区别吗?1. 哪些变动 从上图中,咱们能够概览Vue3的新个性,如下: 速度更快体积缩小更易保护更靠近原生更易使用1.1 速度更快 vue3相比vue2 重写了虚构Dom实现编译模板的优化更高效的组件初始化undate性能进步1.3~2倍SSR速度进步了2~3倍 1.2 体积更小 通过webpack的tree-shaking性能,能够将无用模块“剪辑”,仅打包须要的 可能tree-shaking,有两大益处: 对开发人员,可能对vue实现更多其余的性能,而不用担心整体体积过大对使用者,打包进去的包体积变小了vue能够开发出更多其余的性能,而不用担心vue打包进去的整体体积过多 1.3 更易保护 compositon Api 可与现有的Options API一起应用灵便的逻辑组合与复用Vue3模块能够和其余框架搭配应用 更好的Typescript反对 VUE3是基于typescipt编写的,能够享受到主动的类型定义提醒 1.4 编译器重写 1.5 更靠近原生 能够自定义渲染 API 1.6 更易使用 响应式 Api 裸露进去 轻松辨认组件从新渲染起因 2. Vue3新增个性 Vue 3 中须要关注的一些新性能包含: framentsTeleportcomposition ApicreateRenderer2.1 framents 在 Vue3.x 中,组件当初反对有多个根节点 <!-- Layout.vue --><template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer></template>2.2 Teleport Teleport 是一种可能将咱们的模板挪动到 DOM 中 Vue app 之外的其余地位的技术,就有点像哆啦A梦的“任意门” ...

February 20, 2023 · 5 min · jiezi

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

背景看了一些自定义指令的文章,然而探索其原理的文章却不多见,所以我决定水一篇。 如何自定义指令?其实对于这个问题官网文档上曾经有了很好的示例的,咱们先来温故一下。 除了外围性能默认内置的指令 (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 来进行。 ...

February 20, 2023 · 4 min · jiezi

关于vue.js:解决vue项目打包上传到服务器加载出现Loading-chunk-n-failed提示错误

如果呈现了该提醒谬误,浏览器后退后退按钮都不可用了,必须要点击刷新按钮页面能力失常加载如图所示:通过查看vue-router文档有一个router.onError的回调函数:原理就是在监听到路由报错信息后主动进行页面刷新,获取最新的代码。具体方法在router目录下的index.js文件中增加代码如下: router.onError((error) => { const pattern = /Loading chunk (\d)+ failed/g const isChunkLoadFailed = error.message.match(pattern) const targetPath = router.history.pending.fullPath if (isChunkLoadFailed) { router.replace(targetPath) }})

February 20, 2023 · 1 min · jiezi

关于vue.js:社招前端经典vue面试题附答案

Vuex 页面刷新数据失落怎么解决体验 能够从localStorage中获取作为状态初始值: const store = createStore({ state () { return { count: localStorage.getItem('count') } }})业务代码中,提交批改状态同时保留最新值:虽说实现了,然而每次还要手动刷新localStorage不太优雅 store.commit('increment')localStorage.setItem('count', store.state.count)答复范例 vuex只是在内存保留状态,刷新之后就会失落,如果要长久化就要存起来localStorage就很适合,提交mutation的时候同时存入localStorage,store中把值取出作为state的初始值即可。这里有两个问题,不是所有状态都须要长久化;如果须要保留的状态很多,编写的代码就不够优雅,每个提交的中央都要独自做保留解决。这里就能够利用vuex提供的subscribe办法做一个对立的解决。甚至能够封装一个vuex插件以便复用。相似的插件有vuex-persist、vuex-persistedstate,外部的实现就是通过订阅mutation变动做对立解决,通过插件的选项管制哪些须要长久化原理 能够看一下vuex-persist (opens new window)外部的确是利用subscribe实现的 Vue 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?你能说说如下代码的实现原理么?1)Vue为什么要用vm.$set() 解决对象新增属性不能响应的问题 Vue应用了Object.defineProperty实现双向数据绑定在初始化实例时对属性执行 getter/setter 转化属性必须在data对象上存在能力让Vue将它转换为响应式的(这也就造成了Vue无奈检测到对象属性的增加或删除)所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 2)接下来咱们看看框架自身是如何实现的呢? Vue 源码地位:vue/src/core/instance/index.jsexport function set (target: Array<any> | Object, key: any, val: any): any { // target 为数组 if (Array.isArray(target) && isValidArrayIndex(key)) { // 批改数组的长度, 防止索引>数组长度导致splcie()执行有误 target.length = Math.max(target.length, key) // 利用数组的splice变异办法触发响应式 target.splice(key, 1, val) return val } // key 曾经存在,间接批改属性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 自身就不是响应式数据, 间接赋值 if (!ob) { target[key] = val return val } // 对属性进行响应式解决 defineReactive(ob.value, key, val) ob.dep.notify() return val}咱们浏览以上源码可知,vm.$set 的实现原理是: ...

February 19, 2023 · 13 min · jiezi

关于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 (这种办法理论批改的是本人的数据 父组件的数据没变) ...

February 19, 2023 · 4 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 监听数据读取和设置数据描述符绑定实现后,咱们就能失去以下的流程图 图中咱们能够看出,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 的响应式原理 ...

February 19, 2023 · 6 min · jiezi

关于vue.js:VueJs中动态更改svg的相关属性

公司我的项目中有一个对于图标库治理的需要,大抵须要在页面可能动静去更改对应svg图标的大小、色彩等(这里的更改色彩限度线性图标)。在网上查找了相干材料,做了技术的预研及demo的编写,在此记录一下。怎么将一个近程的svg图标资源"下载"到本地首页咱们能够利用XMLHttpRequest对象来申请对应的svg图标的近程资源链接地址,并监听实现XMLHttpRequest对象的load事件,将返回的资源进行dom对象的转换、string转换为xml。 代码如下: const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://www.xx.com/img/xxx.svg', true); xhr.send(); /* 监听xhr对象 */ xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseXML, 'xhr.responseXML---------') } }; xhr.addEventListener('load', () => { const resXML = stringToXml(xhr.response); this.svgDom = resXML.documentElement.cloneNode(true); });这里的工具函数stringToXml的残缺代码如下: //将字符串转化成dom对象;string转换为xmlfunction stringToXml (xmlString) { let xmlDoc; if (typeof xmlString == "string") { //FF if (document.implementation.createDocument) { const parser = new DOMParser(); xmlDoc = parser.parseFromString(xmlString, "text/xml"); } else if (window.ActiveXObject) { // eslint-disable-next-line no-undef xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = false; xmlDoc.loadXML(xmlString); } } else { xmlDoc = xmlString; } return xmlDoc;}这样就能够获取到近程svg资源对应的dom构造了。 ...

February 18, 2023 · 2 min · jiezi

关于vue.js:echart-bar分组后自定义tooltip及x轴label

echarts 实现分组柱状图,且小组内自定义每一个bar的label option = { color: ['#3e6591', '#eb7d22', '#d73f45'], grid: { left: 100 }, xAxis: { axisLine: { lineStyle: {color: '#ccc'} }, axisLabel: { textStyle: {color: '#777'} } }, yAxis: [{ inverse: true, splitLine: { show: true }, axisTick: { length: 0, lineStyle: {color: '#ccc'} }, axisLine: { lineStyle: {color: '#ccc'} }, data: ['-', '-', '-', '-', '-'] }], series: [{ type: 'bar', data:[220, 182, 191, 234, 290], label: { normal: { show: true, position: 'left', textStyle: {color: '#000'}, formatter: '合同金额', } } }, { type: 'bar', data:[210, 132, 91, 204, 220], label: { normal: { show: true, position: 'left', textStyle: {color: '#000'}, formatter: '已收款', } } }, { type: 'bar', data:[120, 132, 131, 254, 278], label: { normal: { show: true, position: 'left', textStyle: {color: '#000'}, formatter: '应收款', } } }]};例如:相干代码: ...

February 17, 2023 · 3 min · jiezi

关于vue.js:vue获取目录下的文件名

新创建一个vue我的项目,获取views目录下的以.vue结尾的文件的文件名 mounted() { //参数:1.门路;2.是否遍历子目录;3.正则表达式 const files = require.context("@/views", true, /\.vue$/).keys(); console.log(files);}如:要获取views目录下的以.png或.jpg或.jpeg结尾的文件的文件名,可写成 const files = require.context("@/views", true, /\.png|jpg|jpeg$/).keys();

February 17, 2023 · 1 min · jiezi

关于vue.js:Vue3vite项目在局域网内通过ip在手机端访问

应用vite启动我的项目后,想在手机端拜访我的项目,此时是拜访不到的在网上查阅了相干内容,小结一下1.批改vite.confifg,js(未应用ts,应用的批改vite,config.ts) export default defineConfig({ plugins: [vue()], server:{ host:'0.0.0.0' }})这时候还是拜访不了,此时能够敞开共有网络的防火墙而后,查看本人pc在网络中的ip,关上cmd或者terminal ipconfig而后再手机端通过ip:端口(我的项目运行端口),即可拜访

February 17, 2023 · 1 min · jiezi

关于vue.js:Vue2-响应式源码解读

要理解 Vue2 响应式零碎原理,咱们要思考两个问题: 当咱们扭转组件的状态时,零碎会产生什么变动?零碎是如何晓得哪些局部依赖于这个状态?实际上,组件的渲染、计算属性、组件watch对象和Vue.&watch()办法,它们之所以能响应组件props和data的变动,都是围绕着Watcher类来实现的。 本文只截取局部外围代码,重在解说响应式原理,尽量减少其它代码的烦扰,但会正文代码起源,联合源码观看风味更佳。另外,本文源码版本: "version": "2.7.14",定义响应式属性首先,看看组件的props和data中的属性是如何定义为响应式的: // src/core/instance/init.tsVue.prototype._init = function (options?: Record<string, any>) { const vm: Component = this initState(vm) // 初始化状态}// src/core/instance/state.tsexport function initState(vm: Component) { const opts = vm.$options initProps(vm, opts.props) // 初始化Props initData(vm) // 初始化Data initComputed(vm, opts.computed) initWatch(vm, opts.watch)}function initProps(vm: Component, propsOptions: Object) { const props = (vm._props = shallowReactive({})) for (const key in propsOptions) { defineReactive(props, key, value) // 定义响应式属性 }}function initData(vm: Component) { let data: any = vm.$options.data data = vm._data = isFunction(data) ? getData(data, vm) : data || {} observe(data)}// src/core/observer/index.tsexport function observe(value: any, shallow?: boolean, ssrMockReactivity?: boolean) { return new Observer(value, shallow, ssrMockReactivity)}export class Observer { constructor(public value: any, public shallow = false, public mock = false) { const keys = Object.keys(value) for (let i = 0; i < keys.length; i++) { const key = keys[i] // 定义响应式属性 defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock) } }}从下面代码能够看出,在组件初始化阶段,无论是props还是data属性,最终都通过函数defineReactive定义为响应式属性。所以咱们要重点关注这个办法: ...

February 17, 2023 · 5 min · jiezi

关于vue.js:如何优化-Vuejs-应用程序

单页面利用(SPAs)当解决实时、异步数据时,能够提供丰盛的、可交互的用户体验。但它们也可能很重,很臃肿,而且性能很差。在这篇文章中,咱们将介绍一些前端优化技巧,以放弃咱们的Vue应用程序绝对精简,并且只在须要的时候提供必须的JS。 留神:这里假如你对Vue和Composition API有肯定的相熟水平,但无论你抉择哪种框架,都心愿能有一些播种。本文作者是一名前端开发工程师,职责是构建Windscope应用程序。上面介绍基于该程序所做的一系列优化。 抉择框架咱们抉择的JS框架是Vue,局部起因是它是我最相熟的框架。以前,Vue与React相比,整体包规模较小。然而,自从最近的React更新以来,均衡仿佛曾经转移到React身上。这并不重要,因为咱们将在本文中钻研如何只导入咱们须要的货色。这两个框架都有优良的文档和宏大的开发者生态系统,这是另一个思考因素。Svelte是另一个可能的抉择,但因为不相熟,它须要更平缓的学习曲线,而且因为较新,它的生态系统不太发达。 Vue Composition APIVue 3引入了Composition API,这是一套新的API用于编写组件,作为Options API的代替。通过专门应用Composition API,咱们能够只导入咱们须要的Vue函数,而不是整个包。它还使咱们可能应用组合式函数编写更多可重用的代码。应用Composition API编写的代码更适宜于最小化,而且整个应用程序更容易受到tree-shaking的影响。 留神:如果你正在应用较老版本的Vue,依然能够应用Composition API:它已被补丁到Vue 2.7,并且有一个实用于旧版本的官网插件。 导入依赖一个要害指标是缩小通过客户端下载的初始JS包的尺寸。Windscope宽泛应用D3进行数据可视化,这是一个宏大的库,范畴很广。然而,Windscope只须要应用D3的一部分。 让咱们的依赖包尽可能小的一个最简略的办法是,只导入咱们须要的模块。 让咱们来看看D3的selectAll函数。咱们能够不应用默认导入,而只从d3-selection模块中导入咱们须要的函数: // Previous:import * as d3 from 'd3'// Instead:import { selectAll } from 'd3-selection'代码宰割有一些包在整个Windscope的很多中央都有应用,比方AWS Amplify认证库,特地是Auth办法。这是一个很大的依赖,对咱们的JS包的大小有很大奉献。比起在文件顶部动态导入模块,动静导入容许咱们在代码中须要的中央精确导入模块。 比起这么导入: import { Auth } from '@aws-amplify/auth'const user = Auth.currentAuthenticatedUser()咱们能够在想要应用它的中央导入模块: import('@aws-amplify/auth').then(({ Auth }) => { const user = Auth.currentAuthenticatedUser()})这意味着该模块将被宰割成一个独自的JS包(或 "块"),只有该模块被应用时,才会被浏览器下载。 除此之外,浏览器能够缓存这些依赖,比起应用程序的其余局部代码,这些模块根本不会扭转。 懒加载咱们的应用程序应用Vue Router作为导航路由。与动静导入相似,咱们能够懒加载咱们的路由组件,这样就能够在用户导航到路由时,它们才会被导入(连同其相干的依赖关系)。 index/router.js文件: // Previously:import Home from "../routes/Home.vue";import About = "../routes/About.vue";// Lazyload the route components instead:const Home = () => import("../routes/Home.vue");const About = () => import("../routes/About.vue");const routes = [ { name: "home", path: "/", component: Home, }, { name: "about", path: "/about", component: About, },];当用户点击About链接并导航到路由时,About路由所对应的代码才会被加载。 ...

February 16, 2023 · 4 min · jiezi

关于vue.js:如何从0开始搭建-Vue-组件库

作者:京东批发 陈艳春 前言:组件设计是通过对性能及视觉表白中元素的拆解、演绎、重组,并基于可被复用的目标,造成规范化的组件,通过多维度组合来构建整个设计方案,將这些组件整顿在一起,便造成组件库。本文咱们次要讲述基于Vant CLI的自建组件库。Vant CLI 是一个基于 Vite 实现的 Vue 组件库构建工具,通过 Vant CLI 能够疾速搭建一套性能齐备的 Vue 组件库。 建设组件库的意义首先组件库能够给咱们降本提效,其次能够放弃视觉格调对立以及交互统一,能够帮忙咱们疾速构建应用场景,便于多个我的项目后续迭代降级 。 视觉格调对立以及交互的一致性,能够缩小用户学习老本造就用户习惯,让产品领有良好的用户体验。比方一个四级地址的抉择组件,在整个产品中应该就一种交互方式,如果一会是滚动抉择,一会是点击抉择,会让用户操作起来比拟焦躁,对立交互能够缩小用户学习老本。 新产品上线后,还须要一直的去欠缺,在迭代过程中可能会新增其余性能,这时候咱们就能够只批改组件库一套代码,所有不同我的项目雷同组件就能够达到了迭代降级的成果。 如何创立组件库一、梳理组件清单首先梳理出我的项目中款式雷同的模块,和产品探讨将来会有哪些布局,现有的组件是否可能满足需要,是否须要补充设计方案,清单整顿结束后,将每一个组件建成一个独立工作,像日常需要那样,不便随时更新应用。 二、场景整合把本人变成产品的深度用户,把现有线上产品残缺体验一遍,绘制用户行为门路,并和需求方沟通了解后续打算,将组件的所有的以后/潜在利用场景总结进去,尽可能不脱漏场景。 三、组件库框架选型看了开源的 Vue3 组件库,总结了一些前端目前风行的趋势,列出来多个版本和框架的,本文只探讨 Vue3 版本。 1.element-plus - 经典中的经典,全面反对 Vue 3 2.tdesign-vue-next - 鹅厂优质 UI 组件,配套工具完美,设计工整,文档清晰 3.arco-design-vue- 字节跳动开源 UI 组件库,大厂逻辑,设计文档完满 4.ant-design-vue - 蚂蚁前端 UI 库,面向企业级中后盾 5.naive-ui - 宝藏 Vue UI 库,Vue UI 新星,从 Vue 3 起步 6.vant - 有赞团队开源挪动 UI 组件库,全面反对 Vue 3 7.nutui - 京东出品,挪动端敌对,面向电商业务场景 8.vuetify - 老牌 Vue UI ,基于谷歌的 Material Design 款式开发 ...

February 16, 2023 · 2 min · jiezi

关于vue.js:一个应付需求的拖拽表格

公司PM又搞事了,非要做什么拖拽表格,拖拽表头调整地位不行,还要能调整宽度... 市面上现成的轮子,要么是拖拽地位,要么是拖拽宽度,简略找了找,还是本人造轮子吧,本人造的想怎么捏怎么捏。 需要刚刚开始的时候,依据开源大佬提醒,写了这样的拖拽款式,开开心心的交给PM看,后果被仁慈的驳回,“我要拿得起放得下的表头”:PM如是说。好吧,好吧,我写,我写。 当初需要写好了,我来整顿一下思路吧,看着很难,写起来很难真正做完又不感觉难的一个需要,还是被坑了好些下的。首先要明确需要,表格模式,要能拿起以后点击的表头,而后拖动,当他通过左右两侧任意一个表头一半宽度的时候,在该表头,左&右减少一个2像素的边,松开鼠标,将拿起的表头放到竖线处,这是表头的拖动。其次,表头的宽度能够任意拖动 重点来了,两端须要有固定的列 好了,需要明确了,那么咱们当初开始整顿思路造作吧,饿了么的表格有现成的拖动宽,固定列,那么就用饿了么的表格组件吧,emmm,组长说,用div本人布局吧,开展元素内容过多,用饿了么的组件会有限度。行行行,我都本人写! 先确定表头的数据格式,此处还是要借鉴饿了么,白嫖一时爽,始终白嫖一爽快~ tableHeader [ { label: '表头名称', prop: 'name', // 当前列数据对应字段,很重要 width: '' // 以后宽度 }]通过创立一个数组来存储表头(行),通过调整数组程序来调整列表排序,通过数组内对象属性width来存储当前列宽 <div class="d-table" ref="dTable"> <el-scrollbar ref="el_scroll_dom" class="default-scrollbar" wrap-class="default-scrollbar__wrap" view-class="default-scrollbar__view"> <div class="d-table_header thead flex" :style="[{width:allwidth}]"> <div v-for="(col,index) in tableHeader" :key="col.label" :class="['d-table_th', 'column_' + index]" :style="{ width: parseInt(col.width)+'px'}" @mousedown="handleMouseDown($event, index)" @mousemove="handleMouseMove($event, index)" > {{col.label}} </div> <div class="draggingDom" ref="draggingDom" v-show="draggingDomVisible">{{draggingText}}</div> <div class="resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div> </div> </el-scrollbar></div> 这样,一个表头咱们就渲染进去了,至于办法咱们上面说。 className column_ 用来辨别每一个表头,毕竟操作表头的宽度肯定会操作dom,mousedown 办法为以后鼠标点下事件,即用户操作宽度或调整地位的触发点,mousemove 用来记录鼠标挪动,这里的mousemove次要用来拖动表头宽度时,鼠标款式的成果。 接下来是拖动表头宽度之所以先写这个,起因是整个拖拽的思路都是白嫖了饿了么表格宽度调整首先咱们须要整顿一下,对于拖动都须要哪些元素,这里利用了getBoundingClientRect函数 const tableEl = this.$refs.dTable // 获取外层父级实例 const tableLeft = tableEl.getBoundingClientRect().left // 父级间隔屏幕右边间隔 const columnEl = this.$el.querySelector('.column_'+i) // 获取以后点击元素dom const columnRect = columnEl.getBoundingClientRect() const minLeft = columnRect.left - tableLeft + 30 // 为什么加30? this.dragState = { startMouseLeft: event.clientX, // 鼠标以后x坐标,设置为起始 startLeft: columnRect.right - tableLeft, // 间隔右边初始值 startColumnLeft: columnRect.left - tableLeft, // 表头右边间隔 tableLeft }而后咱们设置鼠标通过时款式变动,在鼠标通过的元素左边间隔8像素的地位,将鼠标指针更改为col-resize,并且将以后点拖拽锁设定为拖拽宽度。 ...

February 14, 2023 · 3 min · jiezi

关于vue.js:Element-组件拓展

Element 作为国内比拟罕用的组件库,尽管组件曾经提供了很多办法供咱们应用,然而也会有遇到对组件进行二次更改的状况,本篇次要记录开发中,晋升Element组件库应用幸福感的一些办法。 1. 下拉选中底部固定项最近遇到个需要,须要在下拉框中减少一个治理选项的按钮,然而组件内并没有提供该api,通过对api的浏览,我发现了el-select的一个办法visible-change,官网是这样形容他的:下拉框呈现/暗藏时触发。 通过select组件下拉框呈现时,咱们能够给这个组件增加一个dom,来作为治理按钮。 <el-select v-model="value" placeholder="请抉择" ref="select" @visible-change="v => visibleChange(v)"> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option></el-select>这是咱们的html局部,上面看如何增加dom // 这里v示意组件下拉框呈现状况visibleChange(v) { let _this = this if(v) { // 首先找到组件的dom构造 const ref = this.$refs['select'];    let popper = ref.$refs.popper;    if (popper.$el) popper = popper.$el; // 接下来,查看该构造下是否曾经存在了治理按钮 if (!Array.from(popper.children).some(v => v.className === 'el-select_groups')) { // 创立dom并增加到组件构造内 const dom = document.createElement('div') dom.className = "el-select_groups" dom.innerHTML = `<i class="icon el-icon-shezhi1" style="margin-right: 4px"></i><span>治理分组</span>` popper.appendChild(dom); // 给治理按钮绑定点击事件 dom.addEventListener('click', function(e) { // 此处写点击事件执行办法 ref.blur() }) } }}这样,select组件的底部治理按钮就写好了,同理Cascader级联选择器同样实用。 ...

February 14, 2023 · 1 min · jiezi

关于vue.js:在vue中使用海康插件实现视频实时监控海康插件

在vue中应用海康插件实现视频实时监控(海康插件)首先下载并装置海康插件 webcontrol.exe 而后间接就写代码把,所有办法都是海康他们间接封装好的demo 初始化之后,后盾把密钥和一些配置的货色返回给你 而后只须要把监控点编号给你就能够了。实现之后简直你想要的操作都有 各种黑科技,而你只需解决这个插件的兼容问题和一些bug,底层他们时用C写得,然而最终实现播放呢,又是webscoket实现的。 代码在index.html中引入上面这三个js <script src="./static/hkjs/jquery-1.12.4.min.js"></script><script src="./static/hkjs/jsencrypt.min.js"></script><script src="./static/hkjs/jsWebControl-1.0.0.min.js"></script>html:采纳了动静获取容器的宽和高 <div id="playWnd" class="playWnd" :style="{width:swfWidth+'px',height:swfHeight+'px'}"></div>data: swfHeight: "", swfWidth: "",//初始化参数 initparam: { appKey: "xxxxx", secret: "xxxxxxx", apiIp: "xxx.xxx.xxx", apiPort: 8099, layout: "1x1"//这里是来管制你一开始初始化之后的分屏的 }, //监控点编号 pointCode: "xxxxxxx", pubKey: "", oWebControl: null, WebControl: nullmethods: //海康插件页面渲染办法 //Html:<div id="playWnd" class="playWnd"></div> 就是放插件的盒子,id不要改,改了的话上面的办法外面也要改 //css:本人写一个盒子,让下面这个盒子100%就好 //js:data,methods,生命周期函数外面也要去看下 //想要用海康视频间接把从onSearch以下的所以办法复制过来 // 后面三个接口是咱们本人要写的 //查问videoList,密钥getInitParam,执行就是videoPreview onSearch() { this.oWebControl.JS_ShowWnd(); this.videoList = []; videoList( 接口参数 ).then(res => { if (res.statusCode == 200) { this.videoList = res.result.rows; } else { this.$message({ message: res.message, type: "warning" }); } }); }, //获取海康密钥 getInitParam() { getInitParam('xxx').then(res => { this.initparam = res; this.init() }); }, //执行每监控点预览的操作 //获取监控点编号 videoPreview(val, index) { this.position = index; this.oWebControl.JS_ShowWnd(); this.pointCode = val.pointCode; this.startpreview(); }, // 创立播放实例 initPlugin() { this.oWebControl = new WebControl({ szPluginContainer: "playWnd", // 指定容器id iServicePortStart: 15900, // 指定起止端口号,倡议应用该值 iServicePortEnd: 15909, szClassId: "23BF3B0A-2C56-4D97-9C03-0CB103AA8F11", // 用于IE10应用ActiveX的clsid // 创立WebControl实例胜利 cbConnectSuccess: () => { this.oWebControl .JS_StartService("window", { // WebControl实例创立胜利后须要启动服务 dllPath: "./VideoPluginConnect.dll" // 值"./VideoPluginConnect.dll"写死 }) .then( () => { // 启动插件服务胜利 this.oWebControl.JS_SetWindowControlCallback({ // 设置音讯回调 cbIntegrationCallBack: this.cbIntegrationCallBack }); this.oWebControl .JS_CreateWnd("playWnd", this.swfWidth, this.swfHeight) .then(() => { //JS_CreateWnd创立视频播放窗口,宽高可设定 this.getInitParam(); // 创立播放实例胜利后初始化 }); }, () => { // 启动插件服务失败 } ); }, // 创立WebControl实例失败 cbConnectError: () => { this.oWebControl = null; $("#playWnd").html("插件未启动,正在尝试启动,请稍候..."); this.WebControl.JS_WakeUp("VideoWebPlugin://"); // 程序未启动时执行error函数,采纳wakeup来启动程序 this.initCount++; if (this.initCount < 3) { setTimeout(() => { this.initPlugin(); }, 3000); } else { console.log("插件启动失败,请查看插件是否装置!"); } }, // 异样断开:bNormalClose = false cbConnectClose: bNormalClose => { // JS_Disconnect失常断开:bNormalClose = true console.log("cbConnectClose"); this.oWebControl = null; } }); }, // 设置窗口管制回调 setCallbacks() { this.oWebControl.JS_SetWindowControlCallback({ cbIntegrationCallBack: this.cbIntegrationCallBack }); }, // 推送音讯 cbIntegrationCallBack(oData) { console.log(oData.responseMsg); /* showCBInfo(JSON.stringify(oData.responseMsg)); */ }, //初始化 init() { this.getPubKey(() => { // 请自行批改以下变量值 let appkey = this.initparam.appKey; //综合安防治理平台提供的appkey,必填 let secret = this.setEncrypt(this.initparam.secret); //综合安防治理平台提供的secret,必填 let ip = this.initparam.apiIp; //综合安防治理平台IP地址,必填 let port = this.initparam.apiPort; //综合安防治理平台端口,若启用HTTPS协定,默认443 port = parseInt(port); let layout = this.initparam.layout; let playMode = 0; //初始播放模式:0-预览,1-回放 let snapDir = "D:\\SnapDir"; //抓图存储门路 let videoDir = "D:\\VideoDir"; //紧急录像或录像剪辑存储门路 let enableHTTPS = 1; //是否启用HTTPS协定与综合安防治理平台交互,是为1,否为0 let encryptedFields = "secret"; //加密字段,默认加密畛域为secret let showToolbar = 1; //是否显示工具栏,0-不显示,非0-显示 let showSmart = 1; //是否显示智能信息(如配置挪动侦测后画面上的线框),0-不显示,非0-显示 let buttonIDs = "0,16,256,257,258,259,260,512,513,514,515,516,517,768,769"; //自定义工具条按钮 // 请自行批改以上变量值 this.oWebControl .JS_RequestInterface({ funcName: "init", argument: JSON.stringify({ appkey: appkey, //API网关提供的appkey secret: secret, //API网关提供的secret ip: ip, //API网关IP地址 playMode: playMode, //播放模式(决定显示预览还是回放界面) port: port, //端口 snapDir: snapDir, //抓图存储门路 videoDir: videoDir, //紧急录像或录像剪辑存储门路 layout: layout, //布局 enableHTTPS: enableHTTPS, //是否启用HTTPS协定 encryptedFields: encryptedFields, //加密字段 showToolbar: showToolbar, //是否显示工具栏 showSmart: showSmart, //是否显示智能信息 buttonIDs: buttonIDs //自定义工具条按钮 }) }) .then(oData => { this.oWebControl.JS_Resize(this.swfWidth, this.swfHeight); // 初始化后resize一次,躲避firefox下首次显示窗口后插件窗口未与DIV窗口重合问题 //startpreview(); //初始化之后开启预览 }); }); }, //获取公钥 getPubKey(callback) { this.oWebControl .JS_RequestInterface({ funcName: "getRSAPubKey", argument: JSON.stringify({ keyLength: 1024 }) }) .then(oData => { console.log(oData); if (oData.responseMsg.data) { this.pubKey = oData.responseMsg.data; callback(); } }); }, //RSA加密 setEncrypt(value) { let encrypt = new JSEncrypt(); encrypt.setPublicKey(this.pubKey); return encrypt.encrypt(value); }, // 设置窗口裁剪,当因滚动条滚动导致窗口须要被遮住的状况下须要JS_CuttingPartWindow局部窗口 setWndCover() { let iWidth = $(window).width(); let iHeight = $(window).height(); let oDivRect = $("#playWnd") .get(0) .getBoundingClientRect(); let iCoverLeft = oDivRect.left < 0 ? Math.abs(oDivRect.left) : 0; let iCoverTop = oDivRect.top < 0 ? Math.abs(oDivRect.top) : 0; let iCoverRight = oDivRect.right - iWidth > 0 ? Math.round(oDivRect.right - iWidth) : 0; let iCoverBottom = oDivRect.bottom - iHeight > 0 ? Math.round(oDivRect.bottom - iHeight) : 0; iCoverLeft = iCoverLeft > this.swfWidth ? this.swfWidth : iCoverLeft; iCoverTop = iCoverTop > this.swfHeight ? this.swfHeight : iCoverTop; iCoverRight = iCoverRight > this.swfWidth ? this.swfWidth : iCoverRight; iCoverBottom = iCoverBottom > this.swfHeight ? this.swfHeight : iCoverBottom; this.oWebControl.JS_RepairPartWindow(0, 0, this.swfWidth+1, this.swfHeight); // 多1个像素点避免还原后边界缺失一个像素条 if (iCoverLeft != 0) { this.oWebControl.JS_CuttingPartWindow(0, 0, iCoverLeft, this.swfHeight); } if (iCoverTop != 0) { this.oWebControl.JS_CuttingPartWindow(0, 0, this.swfWidth+1, iCoverTop); // 多剪掉一个像素条,防止出现剪掉一部分窗口后呈现一个像素条 } if (iCoverRight != 0) { this.oWebControl.JS_CuttingPartWindow( this.swfWidth - iCoverRight, 0, iCoverRight, this.swfHeight ); } if (iCoverBottom != 0) { this.oWebControl.JS_CuttingPartWindow( 0, this.swfHeight - iCoverBottom, this.swfWidth, iCoverBottom ); } }, //视频“预览”性能 startpreview() { let pointCode = this.pointCode; let cameraIndexCode = pointCode; //获取输出的监控点编号值,必填 let streamMode = 0; //奴才码流标识:0-主码流,1-子码流 let transMode = 1; //传输协定:0-UDP,1-TCP let gpuMode = 0; //是否启用GPU硬解,0-不启用,1-启用 let wndId = -1; //播放窗口序号(在2x2以上布局下可指定播放窗口) cameraIndexCode = cameraIndexCode.replace(/(^\s*)/g, ""); cameraIndexCode = cameraIndexCode.replace(/(\s*$)/g, ""); this.oWebControl.JS_RequestInterface({ funcName: "startPreview", argument: JSON.stringify({ cameraIndexCode: cameraIndexCode, //监控点编号 streamMode: streamMode, //奴才码流标识 transMode: transMode, //传输协定 gpuMode: gpuMode, //是否开启GPU硬解 wndId: wndId //可指定播放窗口 }) }); }, //进行全副预览性能 stopAllPreview() { this.oWebControl.JS_RequestInterface({ funcName: "stopAllPreview" }); }, //敞开视频窗口 closeWindow() { if (this.oWebControl != null) { this.oWebControl.JS_HideWnd(); // 先让窗口暗藏,躲避可能的插件窗口滞后于浏览器隐没问题 this.oWebControl.JS_Disconnect().then( () => { // 断开与插件服务连贯胜利 }, () => { // 断开与插件服务连贯失败 } ); } } }, created() { this.getInitParam();//获取初始话所需的参数 }, beforeMount() {this.WebControl = WebControl;},//调用电脑中的插件 mounted() { this.swfHeight = document.getElementById("videoMaincontent1").offsetHeight; this.swfWidth = document.getElementById("videoMaincontent1").offsetWidth; //页面加载时创立播放实例初始化 this.initPlugin(); // 监听resize事件,使插件窗口尺寸追随DIV窗口变动 $(window).resize(() => { if (this.oWebControl != null) { this.oWebControl.JS_Resize(this.swfWidth, this.swfHeight); this.setWndCover(); } }); // 监听滚动条scroll事件,使插件窗口追随浏览器滚动而挪动 $(window).scroll(() => { if (this.oWebControl != null) { this.oWebControl.JS_Resize(this.swfWidth, this.swfHeight); this.setWndCover(); } }); }, beforeDestroy() { this.closeWindow();//敞开插件 }

February 14, 2023 · 4 min · jiezi

关于vue.js:new-Vue的时候到底做了什么

Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创立前后:首先Vue进行生命周期,事件初始化产生在beforeCreate生命周期函数前,而后进行数据监测和数据代理的初始化,也就是创立vm对象的过程,当vm对象创立实现就能够通过vm对象拜访到劫持的数据,比方data中的数据,methods中的办法等。而后Vue调用外部的render函数开始解析模板将其解析为一个JS对象也即在内存中生成虚构DOM也就是Vnode对象。第二阶段是vm对象挂载前后:挂载实现前页面出现的是未通过Vue编译的DOM构造,所有对DOM的操作最终都不会失效。挂载前首先将内存中的Vnode转换为实在DOM插入页面,此时实现挂载。页面中出现的就是通过Vue编译的DOM构造,至此初始化过程完结。 2.开启订阅音讯也就是数据劫持代理监听,其实就是写了一个watcher函数去监听数据的扭转,发送网络申请,绑定自定义事件等初始化操作。当数据发生变化当前即状态变更的时候,会从新结构新的Vnode对象。而后用新的Vnode对象和旧的Vnode对象进行差别比拟也就是DIFF算法,而后把差别利用到旧的Vnode对象所构建的真正的DOM树上这个过程就是patch,视图就更新了 每一个组件在加载时都会调用Vue外部的render函数把该组件的tamplate选项的模板解析为一个JS对象,这个对象和DOM节点对象构造一样,而后是数据劫持代理监听,当数据发生变化当前,将旧Vnode对象和生成的新Vnode对象比拟差别而后更新DOM Vnode: {tag:"", id:, name:"Box", $el:实在页面上的DOM的援用, //等等属性 chiren:[ { tag:"", id:, name:"Box2",$el:实在页面上的DOM的援用, //等等属性 }, { tag:"", id:, name:"Box3",$el:实在页面上的DOM的援用,//等等属性 }] } 什么是DIFFdiff算法是一种比照算法。比照两者是旧虚构DOM和新虚构DOM,比照出是哪个虚构节点更改了,找出这个虚构节点,并只更新这个虚构节点所对应的实在节点,而不必更新其余数据没产生扭转的节点,实现精准地更新实在DOM,进而提高效率 其有两个特点: 比拟只会在同层级进行, 不会跨层级比拟在diff比拟的过程中,循环从两边向两头比拟 DIFF算法的过程: 当数据产生扭转时,订阅者watcher就会调用patch给实在的DOM打补丁通过isSameVnode进行判断,雷同则调用patchVnode办法patchVnode做了以下操作: 找到对应的实在dom,称为el如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点如果oldVnode有子节点而VNode没有,则删除el子节点如果oldVnode没有子节点而VNode有,则将VNode的子节点实在化后增加到el如果两者都有子节点,则执行updateChildren函数比拟子节点updateChildren次要做了以下操作: 设置新旧VNode的头尾指针新旧头尾指针进行比拟,循环向两头聚拢,依据状况调用patchVnode进行patch反复流程、调用createElem创立一个新节点,从哈希表寻找 key统一的VNode 节点再分状况操作 参考 前端进阶面试题具体解答 对于Vue中el,template,render,$mount的渲染渲染根节点: 先判断有无el属性,有的话间接获取el根节点,没有的话调用$mount去获取根节点。渲染模板: 有render:这时候优先执行render函数,render优先级 > template。无render:有template时拿template去解析成render函数的所需的格局,并应用调用render函数渲染。无template时拿el根节点的outerHTML去解析成render函数的所需的格局,并应用调用render函数渲染渲染的形式:无论什么状况,最初都对立是要应用render函数渲染

February 14, 2023 · 1 min · jiezi

关于vue.js:前端二面经典vue面试题总结

Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创立前后:首先Vue进行生命周期,事件初始化产生在beforeCreate生命周期函数前,而后进行数据监测和数据代理的初始化,也就是创立vm对象的过程,当vm对象创立实现就能够通过vm对象拜访到劫持的数据,比方data中的数据,methods中的办法等。而后Vue调用外部的render函数开始解析模板将其解析为一个JS对象也即在内存中生成虚构DOM也就是Vnode对象。第二阶段是vm对象挂载前后:挂载实现前页面出现的是未通过Vue编译的DOM构造,所有对DOM的操作最终都不会失效。挂载前首先将内存中的Vnode转换为实在DOM插入页面,此时实现挂载。页面中出现的就是通过Vue编译的DOM构造,至此初始化过程完结。 2.开启订阅音讯也就是数据劫持代理监听,其实就是写了一个watcher函数去监听数据的扭转,发送网络申请,绑定自定义事件等初始化操作。当数据发生变化当前即状态变更的时候,会从新结构新的Vnode对象。而后用新的Vnode对象和旧的Vnode对象进行差别比拟也就是DIFF算法,而后把差别利用到旧的Vnode对象所构建的真正的DOM树上这个过程就是patch,视图就更新了 每一个组件在加载时都会调用Vue外部的render函数把该组件的tamplate选项的模板解析为一个JS对象,这个对象和DOM节点对象构造一样,而后是数据劫持代理监听,当数据发生变化当前,将旧Vnode对象和生成的新Vnode对象比拟差别而后更新DOM Vnode: {tag:"", id:, name:"Box", $el:实在页面上的DOM的援用, //等等属性 chiren:[ { tag:"", id:, name:"Box2",$el:实在页面上的DOM的援用, //等等属性 }, { tag:"", id:, name:"Box3",$el:实在页面上的DOM的援用,//等等属性 }] } 什么是DIFFdiff算法是一种比照算法。比照两者是旧虚构DOM和新虚构DOM,比照出是哪个虚构节点更改了,找出这个虚构节点,并只更新这个虚构节点所对应的实在节点,而不必更新其余数据没产生扭转的节点,实现精准地更新实在DOM,进而提高效率 其有两个特点: 比拟只会在同层级进行, 不会跨层级比拟在diff比拟的过程中,循环从两边向两头比拟 DIFF算法的过程:参考 前端进阶面试题具体解答 当数据产生扭转时,订阅者watcher就会调用patch给实在的DOM打补丁通过isSameVnode进行判断,雷同则调用patchVnode办法patchVnode做了以下操作: 找到对应的实在dom,称为el如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点如果oldVnode有子节点而VNode没有,则删除el子节点如果oldVnode没有子节点而VNode有,则将VNode的子节点实在化后增加到el如果两者都有子节点,则执行updateChildren函数比拟子节点updateChildren次要做了以下操作: 设置新旧VNode的头尾指针新旧头尾指针进行比拟,循环向两头聚拢,依据状况调用patchVnode进行patch反复流程、调用createElem创立一个新节点,从哈希表寻找 key统一的VNode 节点再分状况操作 对于Vue中el,template,render,$mount的渲染渲染根节点: 先判断有无el属性,有的话间接获取el根节点,没有的话调用$mount去获取根节点。渲染模板: 有render:这时候优先执行render函数,render优先级 > template。无render:有template时拿template去解析成render函数的所需的格局,并应用调用render函数渲染。无template时拿el根节点的outerHTML去解析成render函数的所需的格局,并应用调用render函数渲染渲染的形式:无论什么状况,最初都对立是要应用render函数渲染失败

February 14, 2023 · 1 min · jiezi

关于vue.js:前端一面必会vue面试题总结

Vue 模板编译原理Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步 第一步是将 模板字符串 转换成 element ASTs(解析器)第二步是对 AST 进行动态节点标记,次要用来做虚构DOM的渲染优化(优化器)第三步是 应用 element ASTs 生成 render 函数代码字符串(代码生成器)相干代码如下 export function compileToFunctions(template) { // 咱们须要把html字符串变成render函数 // 1.把html代码转成ast语法树 ast用来形容代码自身造成树结构 不仅能够形容html 也能形容css以及js语法 // 很多库都使用到了ast 比方 webpack babel eslint等等 let ast = parse(template); // 2.优化动态节点 // 这个有趣味的能够去看源码 不影响外围性能就不实现了 // if (options.optimize !== false) { // optimize(ast, options); // } // 3.通过ast 从新生成代码 // 咱们最初生成的代码须要和render函数一样 // 相似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world")))) // _c代表创立元素 _v代表创立文本 _s代表文Json.stringify--把对象解析成文本 let code = generate(ast); // 应用with语法扭转作用域为this 之后调用render函数能够应用call扭转this 不便code外面的变量取值 let renderFn = new Function(`with(this){return ${code}}`); return renderFn;}Vue3.x 响应式数据原理Vue3.x改用Proxy代替Object.defineProperty。因为Proxy能够间接监听对象和数组的变动,并且有多达13种拦挡办法。并且作为新规范将受到浏览器厂商重点继续的性能优化。proxy根本用法 ...

February 14, 2023 · 5 min · jiezi

关于vue.js:基于声网互动白板实现一个多人数独游戏

本文作者是声网社区的开发者“tjss”。他基于Vue、声网的互动白板的代码模板,搭建出了一个反对多人互动的数独游戏。本文记录了他的实现过程,欢送大家也能够尝试实现本人的小游戏或利用。本文波及的声网产品均反对收费注册下载,并为每个开发者提供每月收费 10000 分钟应用额度,欢送大家尝试体验、实际。我基于声网互动白板的 SDK 与 Window Manager 开发了一个场景化窗口插件,实现了一个多人数独游戏。在游戏中,每个玩家进入白板房间,都能看到数独游戏插件,同时能够参加其中,与房间内的小伙伴一起实现数独解题。 后期筹备1、注册一个声网账号,并实名认证,以便在后盾创立我的项目,获取开发时会用到的 Appid 和 Token 2、理解 Vue 开发的基础知识。 3、理解声网互动白板产品,一些根本的接口和性能,看官网文档就足够了。 4、搭建开发环境: NodeJs 版本 16.0.0@Vue/cli 4.5.1VSCode 代码开发工具开启和配置互动白板服务首先咱们须要一个实名认证的声网账号,进入控制台(console.agora.io),在声网控制台开启互动白板服务。这里须要留神的是互动白板是作为服务而显示的,而控制台中只是显示我的项目列表,并没有间接显示服务。 这时候咱们就先创立一个我的项目,而后点击“配置”进入到我的项目详情中,在外面的页面就能够看到服务内容了。 这时候找到互动白板服务,点击开启就行。因为我的曾经开启了,所以显示的是配置按钮。 应用官网提供的代码模板场景化窗口插件目前咱们不须要从零开始建设的,声网提供了一个代码模板,基于此模板咱们能够很轻松就能实现一个在互动白板上应用的插件。 模板地址:https://github.com/netless-io... 当搭建好开发环境后,便能够下载模板代码了,通过 Git 或者下载 Zip 都行。 须要留神 README.md 中的开发环境配置: 1、在 .env 文件里配置白板房间 UUID 和 Token请将本目录下的 .env.example 文件复制一份,重命名为 .env 或 .env.local 后,在外面填写必须的白板配置信息。你能够在声网互动白板官网的 Netless Workshop 申请专用的白板配置。 2、执行 npm install 装置依赖 3、执行 npm start 进行本地开发 我的项目构造笔者是基于 Vue 版本的插件模板进行开发的,间接关上我的项目,批改 src 里的内容即可,基本上和 Vue 开发统一。如果是通过 Git 命令拉取的代码,须要切换分支为 Vue 分支。 ...

February 13, 2023 · 2 min · jiezi

关于vue.js:高级前端二面vue面试题持续更新中

action 与 mutation 的区别 mutation 是同步更新, $watch 严格模式下会报错 action 是异步操作,能够获取数据后调用 mutation 提交最终数据MVVM的优缺点?长处: 拆散视图(View)和模型(Model),升高代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)能够独⽴于Model变动和批改,⼀个ViewModel能够绑定不同的"View"上,当View变动的时候Model不能够不变,当Model变动的时候View也能够不变。你能够把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑提⾼可测试性: ViewModel的存在能够帮忙开发者更好地编写测试代码⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放毛病: Bug很难被调试: 因为使⽤双向绑定的模式,当你看到界⾯异样了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得⼀个地位的Bug被疾速传递到别的地位,要定位原始出问题的地⽅就变得不那么容易了。另外,数据绑定的申明是指令式地写在View的模版当中的,这些内容是没方法去打断点debug的⼀个⼤的模块中model也会很⼤,尽管使⽤⽅便了也很容易保障了数据的⼀致性,过后⻓期持有,不开释内存就造成了破费更多的内存对于⼤型的图形应⽤程序,视图状态较多,ViewModel的构建和保护的老本都会⽐较⾼。形容下Vue自定义指令在 Vue2.0 中,代码复用和形象的次要模式是组件。然而,有的状况下,你依然须要对一般 DOM 元素进行底层操作,这时候就会用到自定义指令。个别须要对DOM元素进行底层操作时应用,尽量只用来操作 DOM展现,不批改外部的值。当应用自定义指令间接批改 value 值时绑定v-model的值也不会同步更新;如必须批改能够在自定义指令中应用keydown事件,在vue组件中应用 change事件,回调中批改vue数据; (1)自定义指令根本内容 全局定义:Vue.directive("focus",{})部分定义:directives:{focus:{}}钩子函数:指令定义对象提供钩子函数 o bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。 o inSerted:被绑定元素插入父节点时调用(仅保障父节点存在,但不肯定已被插入文档中)。 o update:所在组件的VNode更新时调用,然而可能产生在其子VNode更新之前调用。指令的值可能产生了扭转,也可能没有。然而能够通过比拟更新前后的值来疏忽不必要的模板更新。 o ComponentUpdate:指令所在组件的 VNode及其子VNode全副更新后调用。 o unbind:只调用一次,指令与元素解绑时调用。 钩子函数参数o el:绑定元素 o bing: 指令外围对象,形容指令全副信息属性 o name o value o oldValue o expression o arg o modifers o vnode 虚构节点 o oldVnode:上一个虚构节点(更新钩子函数中才有用) (2)应用场景 一般DOM元素进行底层操作的时候,能够应用自定义指令自定义指令是用来操作DOM的。只管Vue推崇数据驱动视图的理念,但并非所有状况都适宜数据驱动。自定义指令就是一种无效的补充和扩大,不仅可用于定义任何的DOM操作,并且是可复用的。(3)应用案例 高级利用: 鼠标聚焦下拉菜单绝对工夫转换滚动动画高级利用: 自定义指令实现图片懒加载自定义指令集成第三方插件Vue-Router 的懒加载如何实现非懒加载: import List from '@/components/list.vue'const router = new VueRouter({ routes: [ { path: '/list', component: List } ]})(1)计划一(罕用):应用箭头函数+import动静加载 ...

February 13, 2023 · 14 min · jiezi

关于vue.js:百度前端常考vue面试题附答案

怎么实现路由懒加载呢这是一道应用题。当打包利用时,JavaScript 包会变得十分大,影响页面加载。如果咱们能把不同路由对应的组件宰割成不同的代码块,而后当路由被拜访时才加载对应组件,这样就会更加高效 // 将// import UserDetails from './views/UserDetails'// 替换为const UserDetails = () => import('./views/UserDetails')const router = createRouter({ // ... routes: [{ path: '/users/:id', component: UserDetails }],})答复范例 当打包构建利用时,JavaScript 包会变得十分大,影响页面加载。利用路由懒加载咱们能把不同路由对应的组件宰割成不同的代码块,而后当路由被拜访的时候才加载对应组件,这样会更加高效,是一种优化伎俩一般来说,对所有的路由都应用动静导入是个好主见给component选项配置一个返回 Promise 组件的函数就能够定义懒加载路由。例如:{ path: '/users/:id', component: () => import('./views/UserDetails') }联合正文 () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue') 能够做webpack代码分块Vue complier 实现模板解析这种事,实质是将数据转化为一段 html ,最开始呈现在后端,通过各种解决吐给前端。随着各种 mv* 的衰亡,模板解析交由前端解决。总的来说,Vue complier 是将 template 转化成一个 render 字符串。能够简略了解成以下步骤:parse 过程,将 template 利用正则转化成AST 形象语法树。optimize 过程,标记动态节点,后 diff 过程跳过动态节点,晋升性能。generate 过程,生成 render 字符串assets和static的区别相同点: assets 和 static 两个都是寄存动态资源文件。我的项目中所须要的资源文件图片,字体图标,款式文件等都能够放在这两个文件下,这是相同点 ...

February 13, 2023 · 8 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 的值。 ...

February 13, 2023 · 2 min · jiezi

关于vue.js:Nuxt3实战系列之了解Nuxt3的渲染模式

应用过nuxt2的人都晓得,nuxt并不单单是个服务端渲染框架,它也同时具备纯客户端渲染以及动态化渲染的能力。在nuxt3上,这三种渲染模式也得以保留,而且还新增了一种hybird模式,也就是能够按路由别离设置渲染模式。 1. 纯客户端渲染开箱即用,一个传统的在浏览器(或客户端)中被渲染的Vue.js应用程序。这种模式下,Vue.js在浏览器下载并解析所有蕴含创立以后界面指令的JavaScript代码后,生成HTML元素。尽管这种技术容许建设简单和动静的UI,并具备平滑的页面过渡,但它有不同的长处和毛病。 长处开发速度快:当齐全在客户端工作时,咱们不用放心代码的服务端兼容问题,例如,通过应用window对象等仅实用于浏览器的API。部署成本低:**运行服务器会减少基础设施的老本,因为你须要在一个反对JavaScript的平台上运行。咱们能够在任何带有HTML、CSS和JavaScript文件的动态服务器上托管纯客户端的应用程序。可离线拜访:**因为代码齐全在浏览器中运行,所以它能够在互联网不可用时很好地放弃工作。 毛病性能:用户必须期待浏览器下载、解析和运行JavaScript文件。这取决于下载局部的网络和解析及执行局部的用户设施,这可能须要一些工夫并影响用户的体验。搜索引擎优化:与服务器渲染的HTML文档相比,通过客户端渲染提供的内容的索引和更新须要更多的工夫。这与咱们探讨的性能缺点无关,因为搜索引擎爬虫在第一次尝试索引页面时不会期待界面齐全出现。应用纯客户端渲染,你的内容在搜寻后果页中的显示和更新将须要更多工夫。 利用场景对于不须要索引或用户频繁拜访的重度交互网络应用,客户端渲染是一个不错的抉择。它能够利用浏览器缓存来跳过后续拜访的下载阶段,如SaaS、Admin管理系统或在线游戏等。 2. 服务端渲染当浏览器申请一个开启了服务端渲染的URL时,服务器会向浏览器返回一个齐全渲染的HTML页面。不论这个页面是当时生成并缓存的,还是即时渲染的,在某个时刻,Nuxt曾经在服务器环境中运行了JavaScript(Vue.js)代码,产生了一个HTML文档。用户立刻失去咱们的应用程序的内容,与客户端渲染相同。这一步相似于PHP或Ruby应用程序所执行的传统服务器端渲染。为了不失去客户端渲染办法的益处,例如动静界面和页面转换,一旦HTML文档被下载,客户端就会在后盾加载服务器上运行的javascript代码。浏览器再次对其进行解释(因而是通用渲染),Vue.js则管制该文档并实现交互性。让动态页面在浏览器中实现交互被称为 "Hydration"。服务端渲染使得Nuxt应用程序有更短的页面加载工夫,同时保留了客户端渲染的长处。此外,因为内容曾经存在于HTML文档中,搜索引擎爬虫能够在没有开销的状况下索引它。 长处性能:用户能够立刻拜访页面的内容,因为浏览器显示动态内容的速度比JavaScript生成的内容快得多。同时,Nuxt在客户端从新渲染过程中保留了网络应用的交互性。搜索引擎优化:通用渲染将页面的整个HTML内容作为一个经典的服务器应用程序提供给浏览器。网络爬虫能够间接对页面内容进行索引,这使得通用渲染成为任何你想疾速索引的内容的最佳抉择。 毛病开发限度:服务器和浏览器环境不提供雷同的API,要写出能在两边无缝运行的代码可能很辣手。侥幸的是,Nuxt提供了指导方针和特定的变量来帮忙你确定一段代码的执行地位。老本:须要启动服务器运行,以便在运行中渲染页面。这就像任何传统的服务器一样减少了每月的老本。然而,因为通用渲染,浏览器接管了客户端的导航,服务器的调用大大减少。 利用场景通用渲染的用处十分宽泛,简直能够适宜任何应用状况,特地适宜任何面向内容的网站:博客、商城、电子商务网站等。客户端渲染和服务端渲染是在浏览器中显示界面的不同策略。默认状况下,Nuxt应用通用渲染(服务端渲染)来提供更好的用户体验和性能,并优化搜索引擎的索引,但你能够在一行配置中切换渲染模式。 export default defineNuxtConfig({ ssr: false // ...other setting}) 3. 预渲染应用nuxi generate命令来构建应用程序。对于每一个页面,Nuxt应用爬虫来生成相应的HTML和有效载荷文件。构建的文件将在.output/public目录下生成。你能够手动指定Nitro在构建过程中获取和预渲染的路线。 defineNuxtConfig({ nitro: { prerender: { routes: ['/user/1', '/user/2'] } }})预渲染能够解决纯动态渲染的毛病,即能保障用户拜访时页面加载比拟迅速,也能让搜索引擎爬虫对页面内容进行索引。不过预渲染针对动静路由在解决上会比拟辣手。 4. 混合渲染(Nuxt3新增)在大多数状况下,Nuxt 2中执行的通用渲染提供了良好的用户和开发者体验。然而,Nuxt 3通过引入混合渲染和cdn渲染,使通用渲染更进一步。混合渲染容许应用路由规定为每条路由制订不同的渲染规定,并决定服务器应如何回应给定URL上的新申请。以前Nuxt应用程序和服务器的每个路由/页面都必须应用雷同的渲染模式,客户端或服务端。然而在各种状况下,有些页面能够在构建时生成,而有些页面应该在客户端渲染。例如,想想一个带有治理局部的内容网站。每一个内容页面应该次要是动态的,并且只生成一次,然而治理局部须要注册,并且体现得更像一个动静应用程序。从rc.12开始的Nuxt 3带有公共测试版的路由规定和混合渲染反对。应用路由规定,你能够为一组nuxt路由定义规定,扭转渲染模式,或依据路由指定一个缓存策略! Nuxt服务器将主动注册相应的中间件,并应用nitro缓存层将路由与缓存处理程序打包。只有有可能,路由规定将被主动利用到部署平台的本地规定(目前反对Netlify和Vercel)。 redirect - 定义服务器端的重定向。ssr - 禁用服务器端对你的应用程序的局部进行渲染,并使其成为SPA专用,应用ssr: false。cors - 用cors: true主动增加cors头文件 - 你能够用headers笼罩自定义输入。headers - 在你的网站上增加特定的题目。static - static反对繁多(按需)构建;swr - swr反对动态构建,继续一个可配置的TTL。(目前在Netlify上能够实现齐全增量的动态生成,Vercel行将推出)。示例如下: export default defineNuxtConfig({ routeRules: { // Static page generated on-demand, revalidates in background '/blog/**': { swr: true }, // Static page generated on-demand once '/articles/**': { static: true }, // Set custom headers matching paths '/_nuxt/**': { headers: { 'cache-control': 's-maxage=0' } }, // Render these routes with SPA '/admin/**': { ssr: false }, // Add cors headers '/api/v1/**': { cors: true }, // Add redirect headers '/old-page': { redirect: '/new-page' }, '/old-page2': { redirect: { to: '/new-page', statusCode: 302 } } }}) ...

February 11, 2023 · 1 min · jiezi

关于vue.js:vueelement封装upLoad

1.开发环境 vue22.电脑系统 windows11专业版3.在开发的过程中,咱们会须要应用到上传的组件;当有多个页面须要应用到上传的时候就比拟麻烦;咱们能够再次封装。4.废话不多说,间接上代码: // 创立 vueFileUpload.vue文件<template> <el-upload action="" :on-change="(file, fileList)=>{FileChange(file, fileList,{type:FileUploadType})}" :show-file-list="false" :multiple="IsMultiple" :auto-upload="false" accept=".txt" ref="fileUpload" > </el-upload></template><script>export default { name: "vueFileUpload", props:{ // 文件上传 FileChange:{ default() { return function () {} ; } }, // 是否多选 IsMultiple :{ default () { return true; } }, FileUploadType:{ default () { return "Single"; } } }, computed:{ dataChange () { const {IsMultiple} = this; return {IsMultiple}; } }, watch:{ dataChange:{ handler(newValue,oldValue) { // console.log("监听到了数据的变动",newValue); this.$refs.fileUpload.$children[0].$refs.input.webkitdirectory = newValue.IsMultiple ? true : false; }, deep: true } },};</script>5.在页面中应用办法如下: ...

February 10, 2023 · 1 min · jiezi

关于vue.js:vue导出txt文件xlsx和vxetable

1.开发环境 vue2.电脑系统 windows11专业版3.咱们可能会须要导出文件的性能,上面我来分享一下在vue中的办法。4.应用xlsx的办法: import XLSX from 'xlsx';<el-button type="primary" size="small" @click="saveText">导出</el-button>saveText () { const header = [ ['性别', '年龄','注册工夫'] ]; this.dataList.unshift(...header); this.downloadXlsx(this.dataList, "数据统计.txt"); }, downloadXlsx (dataList, fileName){ // 参考数据 // dataList = [ // ['姓名', '性别', '年龄', '注册工夫'], // ['张三', '男', 18, new Date()], // ['李四', '女', 22, new Date()] // ]; const stringToBuff = str => { var buf = new ArrayBuffer(str.length); var view = new Uint8Array(buf); for (var i = 0; i !== str.length; ++i) { view[i] = str.charCodeAt(i) & 0xff; } return buf; }; // 创建表格 let workbook = XLSX.utils.book_new(); let worksheet = XLSX.utils.aoa_to_sheet(dataList); XLSX.utils.book_append_sheet(workbook, worksheet, 'sheet1'); // 创立二进制对象写入转换好的字节流 let xlsxBlob = new Blob( [ stringToBuff( XLSX.write(workbook, { bookType: 'txt', bookSST: false, type: 'binary' }) ) ], { type: '' } ); const a = document.createElement('a'); // 利用URL.createObjectURL()办法为a元素生成blob URL a.href = URL.createObjectURL(xlsxBlob); // 创建对象超链接 a.download = fileName; a.click(); },5.应用vxe-table: ...

February 10, 2023 · 1 min · jiezi

关于vue.js:js实现加密解密签名

https://stackoverflow.com/que... https://segmentfault.com/q/10...https://github.com/starmcc/AE... https://www.cnblogs.com/Im-Vi...https://blog.csdn.net/json_li... https://juejin.cn/post/696314...

February 9, 2023 · 1 min · jiezi

关于vue.js:若依分离版370子组件根据不同需要修改父组件appmain的背景颜色

若依拆散版(3.7.0),子组件依据不同须要批改父组件app-main的背景色彩。默认色彩是通明,ABC三个页面,AC用默认,B须要更改为EAEEF4。办法如下:1.src/store/modules/user.js减少如下代码: ....const user = { state: { .... appMainBg: 'transparent' }, mutations: { .... SET_APP_MAIN_BG: (state, appMainBg) => { state.appMainBg = appMainBg }, },2.src/permission.js,路由跳转就重置状态appMainBg值为默认 router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { store.commit('SET_APP_MAIN_BG', 'transparent'); ....3.src/layout/components/AppMain.vue <template> <section class="app-main" :style="'background-color: '+appMainBg+';'"> .... </section></template><script>export default { computed: { // 减少如下代码 appMainBg() { console.log(this.$store.getters.appMainBg); return this.$store.state.user.appMainBg }, .... }}</script>4.指标页面B。 export default { .... created() { this.$store.commit('SET_APP_MAIN_BG', '#EAEEF4'); },

February 9, 2023 · 1 min · jiezi

关于vue.js:vueelement-upload组件上传多次调用接口解决方案

1.开发环境 vue+element2.电脑系统 windows11专业版3.在应用element的过程中,遇到upload组件上传屡次调用接口,上面我来分享一下解决办法: // tempalte代码<el-upload action="" multiple :on-change="FileChange" :show-file-list="false"> <el-button size="small" type="primary">新增</el-button></el-upload>//return 代码:FileUploadNumber:0,fileList:[],FileChange (file, fileList){ console.log(file); console.log(fileList); this.fileList = fileList; this.FileUploadNumber += 1; let length = fileList.length; this.FileUploadNumber = Math.max(length, this.FileUploadNumber); // setTimeout(() => { // // }); if (length !== this.FileUploadNumber) { // console.log('以后length为', length, '不是最大值'); return false; } else { console.log('以后length为最大值', length); //这里就能够调用你上传文件的接口了 const formData = new FormData(); this.fileList instanceof Array && this.fileList.map(item=>{ formData.append("sdkSymbolFiles",item.raw); }); } },4.本期的分享到了这里就完结啦,心愿对你有所帮忙,让咱们一起致力走向巅峰。

February 3, 2023 · 1 min · jiezi

关于vue.js:ElementPlus-Upload上传文件只能上传一次第二次无响应彻底解决

**问题形容:应用elementPlus upload组件上传文件,每次只能抉择一个文件,如果上传失败,再次点击上传按钮,没有反馈。解决:清空已选文件列表,而后从新赋值。** <el-upload :action="uploadFileUrl+'?name='+(downloadFileName.name)" :headers="accessToken" :limit="1" :format="['xls','xlsx']" accept=".xls, .xlsx" :show-file-list="true" :auto-upload="false" ref="uploadRef" :on-success="handleSuccess" :on-change="fileListChange">咱们在on-success中解决 const handleSuccess = (response, file) => { // 上传胜利当前 uploadRef.value.clearFiles() // 先删除抉择的文件 nextTick(() => { // file外面的raw就是File类型,间接复制给组件 uploadRef.value.handleStart(file.raw) // 从新赋值原来的文件 })})

January 31, 2023 · 1 min · jiezi

关于vue.js:qiankun微应用中antd样式问题解决方案

在子利用与主利用对接过程中,页面能够失常展现,然而ant组件显示异样,弹框、下拉框等须要挂载的组件挂载地位异样,导致页面的性能、布局、款式异样。 a-select减少属性: :getPopupContainer="t => t.parentElement"a-tree-select减少属性: :getPopupContainer="t => t.parentElement"a-range-picker减少属性: :get-calendar-container="t => t.parentElement"a-drawer减少属性: :get-container="false"a-modal减少属性: :get-container="() => $el"

January 30, 2023 · 1 min · jiezi

关于vue.js:感受-Vue3-的魔法力量

作者:京东科技 牛至伟 近半年有幸参加了一个翻新我的项目,因为没有任何历史包袱,所以抉择了Vue3技术栈,总体来说感触如下: • setup语法糖<script setup lang="ts">解脱了书写申明式的代码,用起来很晦涩,晋升不少效率 • 能够通过Composition API(组合式API)封装可复用逻辑,将UI和逻辑拆散,进步复用性,view层代码展现更清晰 • 和Vue3更搭配的状态治理库Pinia,少去了很多配置,应用起来更便捷 • 构建工具Vite,基于ESM和Rollup,省去本地开发时的编译步骤,然而build打包时还是会编译(思考到兼容性) • 必备VSCode插件Volar,反对Vue3内置API的TS类型推断,然而不兼容Vue2,如果须要在Vue2和Vue3我的项目中切换,比拟麻烦 当然也遇到一些问题,最典型的就是响应式相干的问题 响应式篇本篇次要借助watch函数,了解ref、reactive等响应式数据/状态,有趣味的同学能够查看Vue3源代码局部加深了解, watch数据源能够是ref (包含计算属性)、响应式对象、getter 函数、或多个数据源组成的数组 import { ref, reactive, watch, nextTick } from 'vue'//定义4种响应式数据/状态//1、ref值为根本类型const simplePerson = ref('张三') //2、ref值为援用类型,等价于:person.value = reactive({ name: '张三' })const person = ref({ name: '张三'})//3、ref值蕴含嵌套的援用类型,等价于:complexPerson.value = reactive({ name: '张三', info: { age: 18 } })const complexPerson = ref({ name: '张三', info: { age: 18 } })//4、reactiveconst reactivePerson = reactive({ name: '张三', info: { age: 18 } })//扭转属性,察看以下不同情景下的监听后果nextTick(() => { simplePerson.value = '李四' person.value.name = '李四' complexPerson.value.info.age = 20 reactivePerson.info.age = 22})//情景一:数据源为RefImplwatch(simplePerson, (newVal) => { console.log(newVal) //输入:李四})//情景二:数据源为'张三'watch(simplePerson.value, (newVal) => { console.log(newVal) //非法数据源,监听不到且控制台告警 })//情景三:数据源为RefImpl,然而.value才是响应式对象,所以要加deepwatch(person, (newVal) => { console.log(newVal) //输入:{name: '李四'}},{ deep: true //必须设置,否则监听不到外部变动}) //情景四:数据源为响应式对象watch(person.value, (newVal) => { console.log(newVal) //输入:{name: '李四'}})//情景五:数据源为'张三'watch(person.value.name, (newVal) => { console.log(newVal) //非法数据源,监听不到且控制台告警 })//情景六:数据源为getter函数,返回根本类型watch( () => person.value.name, (newVal) => { console.log(newVal) //输入:李四 })//情景七:数据源为响应式对象(在Vue3中状态都是默认深层响应式的)watch(complexPerson.value.info, (newVal, oldVal) => { console.log(newVal) //输入:Proxy {age: 20} console.log(newVal === oldVal) //输入:true}) //情景八:数据源为getter函数,返回响应式对象watch( () => complexPerson.value.info, (newVal) => { console.log(newVal) //除非设置deep: true或info属性被整体替换,否则监听不到 })//情景九:数据源为响应式对象watch(reactivePerson, (newVal) => { console.log(newVal) //不设置deep: true也能够监听到 })总结: ...

January 28, 2023 · 4 min · jiezi

关于vue.js:Vue3源码响应式系统依赖收集和派发更新流程浅析

本文基于Vue 3.2.30版本源码进行剖析为了减少可读性,会对源码进行删减、调整程序、扭转的操作,文中所有源码均可视作为伪代码因为ts版本代码携带参数过多,大部分伪代码会采取js的模式展现,而不是原来的ts代码本文重点关注依赖收集和派发更新的流程,为了简化流程,会以Object为根底剖析响应式的流程,不会拘泥于数据类型不同导致的依赖收集和派发更新逻辑不同本文内容Proxy和Reflect的介绍整体依赖收集的流程图以及针对流程图的相干源码剖析整体派发更新的流程图以及针对流程图的相干源码剖析ReactiveEffect外围类的相干源码剖析和流程图展现computed的相干剖析,分为流程图和针对流程图的相干源码剖析watch和watchEffect的初始化源码剖析,包含各种配置参数以及schedulewatch和watchEffect依赖收集和派发更新流程图展现每一个点都有配套的流程图,源码剖析是为流程图服务,联合流程图看源码剖析成果更佳前置常识在Vue2源码-响应式原理浅析的文章剖析中,咱们晓得Vue2应用的是Object.defineProperty的形式,而在Vue3中应用Proxy进行代替数据的响应式劫持,上面将简略介绍Vue3中所应用的Proxy和Reflect Proxy介绍摘录于Proxy - JavaScript | MDNProxy 对象用于创立一个对象的代理,从而实现基本操作的拦挡和自定义(如属性查找、赋值、枚举、函数调用等)。 Proxy只能代理对象,不能代理非对象值,比方String、Number、String、undefined、null等原始值根底语法// handler还有其它办法,上面示例只摘取Vue3中比拟罕用的5个办法const handler = { get(target: Target, key: string | symbol, receiver: object) { }, set(target: object, key: string | symbol, value: unknown, receiver: object) { }, deleteProperty() { }, has() { }, ownKeys() { }}const p = new Proxy(target, handler);参数target要应用 Proxy 包装的指标对象(能够是任何类型的对象,包含原生数组,函数,甚至另一个代理) handler一个通常以函数作为属性的对象,各属性中的函数别离定义了在执行各种操作时代理 p 的行为 Reflect的介绍摘录于Reflect - JavaScript | MDNReflect 是一个内置的对象,它提供拦挡 JavaScript 操作的办法。这些办法与proxy handlers(en-US)的办法雷同。 罕用办法的语法Reflect.set(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean;Reflect.get(target: object, propertyKey: PropertyKey, receiver?: any): any;在Vue3中的作用Reflect的一些办法等价于Object办法的成果Reflect在Vue3中用来代替对象的一些办法操作,如上面代码所示,间接拜访obj.xxx与Reflect.get(obj, "xxx", obj)的成果是一样的,惟一不同点是Reflect的第三个参数,当与Proxy一起应用时,receiver是代理target的对象 ...

January 20, 2023 · 16 min · jiezi

关于vue.js:vue-文档-jsx-和-slot-content

vue 文档, jsx 和 slot content 应该如何看 vue 文档? 就像 jquery 那么看 jsx 是什么?jsx 是 react 创造用于 js 和 html 混写的语法, jsx map 可编程局部 1 function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> );}(待续) vue slot content 是什么?就是上文中 {numbers.map(...)} 中括号里的局部 <template> <ul> ... // 这里须要依据 numbers 循环打印子组件,那么就能够用 vue slot content </ul></template>

January 18, 2023 · 1 min · jiezi

关于vue.js:万字长文vueexpressmysql带你彻底搞懂项目中的权限控制附所有源码

本文略长,倡议珍藏。线上网站演示成果地址:http://ashuai.work:8891/home GitHub仓库地址:https://github.com/shuirongsh... 所谓的权限,其实指的就是:用户是否能看到,以及是否容许其对数据进行增删改查的操作,因为当初开发我的项目的支流形式是前后端拆散,所以整个我的项目的权限是后端权限管制搭配前端权限管制独特实现的后端权限1. 晓得是哪个用户(角色)发的申请token判断(前端申请头中传过来,通过申请拦截器)userid判断(前端申请头中传过来,通过申请拦截器)cookie判断(用的少)session判断(用的很少)2. 权限设计模式RBAC模式~基于角色的权限访问控制(Role-Based Access Control)是商业系统中最常见的权限治理技术之一。 对于什么是RBAC模式的概念本文这里不做赘述,大家能够这样了解: 2.1 RBAC模式某个零碎有一些人在应用,然而应用这个零碎的这些人分为两个派别。有老板派(老板、老板小姨子、老板小舅子等),也有打工仔派(张三、李四、王二、麻子)。零碎有一些页面用于展现数据,作为老板派的相干工作人员,必定是哪个页面都可能看到,毕竟公司都是老板的,必须什么都能看到而作为打工仔派的工作人员,必定是权限比拟低了,只能看到一个业绩页面(假如业绩页面记录了本人每个月为公司发明的业绩)这个案例中的老板派和打工仔派就是两个角色,而老板、老板小姨子、老板小舅子和张三、李四、王二、麻子这些用户就是隶属于角色的用户用户是角色的具象化体现用户的数量个别都大于角色的数量的(想一下...)所以咱们在做权限管制的时候,只须要管制角色能看到那个页面即可,至于用户就让他隶属于这个角色便是通过用户和角色进行关联,也做到了复用和解耦角色能看到哪个页面,取决于角色和菜单页面进行的关联(通过勾选菜单树关联)步骤如下: 给对应角色赋予(调配)相应菜单权限让新建的用户隶属于某个角色,就是给新建的用户调配一下角色2.2 后端建表失常状况下须要五张表: 菜单表(用于存储系统的菜单信息,如菜单的名字、点击这个菜单要去跳转的路由url、以及这个菜单的icon图标名、菜单id等...)角色表(用于存储系统的角色信息,如角色名、角色id、角色备注等...)角色菜单表(用于存储某个角色能看到菜单id有哪些,一个角色id对应多个菜单id,一对多关系)用户表(用于存储用户隶属于哪个角色,比方老板小舅子就是隶属于老板角色,以及用户名、用户id、用户备注啥的...)组织表(用于记录角色属于哪个组织,再大一些的零碎我的项目会建此表,小我的项目没有也行)三张表也能用 咱们将角色菜单表,糅在角色表中,这样的话,只有新建菜单表、角色表(蕴含角色菜单信息)、用户表,依据用户的用户名和明码进行登录(后端会依据用户名和明码查问到这个用户隶属于那个角色下,从而返回此用户对应角色的菜单信息数据) 本文演示建两张表 为了更好的便于大家了解,本文只新建两张表,一张是菜单表,另一张是角色表(角色表中存此角色能看到的单id),而登录的时候,大家抉择角色登录,其实抉择用户登录从某种意义上来说,也是相当于角色登录。 第一步: 角色登录发申请,后端返回此角色对应的菜单树数据(须要前端进一步加工一下),前端获取菜单树数据当前,将其存到vuex中比方是menuTree数组字段,el-menu组件再取到vuex中的menuTree数组数据应用,依据这个菜单树数据进行主动递归渲染 对于el-menu组件的递归自调用,能够参见笔者之前的这篇文章:https://segmentfault.com/a/11... 若对于组件递归常识忘了,能够参见笔者的这篇文章:https://segmentfault.com/a/11...第二步: 第一步中,实现了el-menu组件的渲染展现,然而还少了路由表树结构数组数据(因为点击菜单须要进行路由跳转),所以咱们还须要再搞一份数据routerTree,这个routerTree数据是依据后端返回的菜单树数据,加工成合乎路由表树结构的数组,这个数组是给路由表应用的 routerTree的值是动静,因为不同角色的routerTree不一样对应的路由表还有动态的,不变的,比方404路由、login路由、首页home路由动态路由前端间接写死即可,动静路由应用router.addRoutes(routerTree)将其增加到路由表中即可至于刷新页面vuex中数据失落,就重发一次申请,或者本地存一份都行的 实际上,刷新页面,并不是vuex中的数据失落,而是,而是vuex中的数据初始化了(回到最后的样子,最后时,vuex中的数据原本就是空的) 通过上述两步骤,一个权限零碎的根本样子构造就进去了,只不过还有一些细节须要解决,这个请持续往下浏览 前端权限细化管制前端权限细化分类大抵能够分为四种: 菜单的权限管制页面的权限管制按钮的权限管制字段的权限管制1.菜单的权限管制(以左侧导航菜单为例)不同角色的用户登录当前,看到的是不同的菜单内容。比方: 一般角色(的用户),只能看到某一个菜单管理员角色能看到所有的菜单以el-menu菜单组件进行举例说明2.页面的权限管制角色没登录时,手动在地址栏输出url地址跳转,就强制用户跳转到登录页角色登录后,手动在地址栏输出不存在的url(或者本人不能看的url)地址跳转,让其跳转404页面某些非凡的页面,还能够应用vue-router中的beforeRouteEnter再进行细化管制假如打工仔张三,想要去看老板的页面,因为本人登录时,后端返回的菜单树中没有老板页面的数据,所以路由表中也没有老板页面的路由,所以地址栏url间接跳转时,就会跳转到一个不存在的路由页面,就不会显示出货色了。 对于路由,大家也能够这样了解:就是当地址栏中输出对应的path即url时,路由表大佬去做对应匹配,匹配到了当前,再去渲染对应的.vue组件从而出现对应的数据内容(匹配不到那就404呗) 3.按钮的权限管制按钮的管制,能够细分为两块:一是 : 是否能看到这个按钮、另外是 : 能看到不过是否能点击这个按钮(按钮是否禁用) 按钮是否展现,取决于是否给角色调配了按钮权限。如有的只能看到查看按钮,有的增删改查按钮都能够看到再一个,咱们能够把按钮当做是一个非凡的菜单页面最初,要有一个规定限度,新增节点时,能够新增菜单节点,也能够新增按钮节点,只不过按钮节点永远是最底层的地位,不能在按钮节点下再去新增页面(无意义操作)不了解下面那句话,请持续往下浏览留神,在我的项目中最好不要应用禁用按钮去管制权限,如果角色用户没有某个按钮的权限,间接删除这个节点即可,比方v-if,或el.parentNode.removeChild(el),因为应用禁用按钮去管制权限存在肯定危险,如笔者的这篇文章:https://segmentfault.com/a/11... 非凡状况下,应用禁用按钮也能够去调配管制权限,具体情况具体分析 4. 字段的权限管制如下的表格: 姓名年龄他乡喜好孙悟空500花果山大闹天宫比方年龄字段是隐衷,只有老板能看到员工的年龄,老板的小姨子和小舅子都不能看到,这个需要的实现能够前端依据角色id进行过滤;或者后端再建表做映射关系,返回给前端。 篇幅起因,这里不赘述了。后续闲暇了,笔者再写一篇介绍 后端建菜单表和角色表咱们先从后端开始写,对于每个字段的意思,笔者在代码中提到了 1. 菜单表两张表都在代码中,大家浏览完文章当前,能够在GitHub仓库中自行获取 1.1菜单表数据库截图 这里有几个字段须要着重介绍一下: 1.2pid字段pid字段,即为:parentId是父级节点字段,就是以后节点的父节点,后端在数据库中存储前端菜单树数据时,是不会存成树结构的数据的(JSON模式除外,但这种形式很少用)后端存储的数据是:把树结构的数据铺平(拍平)当前的数据。 比方咱们有这样一个树结构数据 let treeData = [ { id: 1, name: '中国', children: [ // 有的后端喜爱应用child字段,一个意思 { id: 3, name: '北京', }, { id: 4, name: '上海', children: [ { id: 6, name: '浦东新区' } ] }, ] }, { id: 2, name: '美国', children: [ // 有的后端喜爱应用child字段,一个意思 { id: 5, name: '纽约', }, ] },]数据库中可不会间接存一个树结构,数据库会把树结构拍平存起来,即这样存储: ...

January 15, 2023 · 8 min · jiezi

关于vue.js:AntDesignVue1-table实现虚拟滚动

a-virtual-table基于Ant-Design-Vue的 Table 组件开发的虚构滚动组件,反对动静高度,解决数据量大时滚动卡顿的问题。 demo & 源码:https://xiaocheng555.github.i... <a-virtual-table> 组件<template> <div> <a-table v-bind="$attrs" v-on="$listeners" :pagination="false" :columns="tableColumns" :data-source="renderData"> <template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="text"> <slot :name="slot" v-bind="typeof text === 'object' ? text : {text}"></slot> </template> </a-table> <div class="ant-table-append" ref="append" v-show="!isHideAppend"> <slot name="append"></slot> </div> </div></template><script>import throttle from 'lodash/throttle'import Checkbox from 'ant-design-vue/lib/checkbox'import Table from 'ant-design-vue/lib/table'// 判断是否是滚动容器function isScroller (el) { const style = window.getComputedStyle(el, null) const scrollValues = ['auto', 'scroll'] return scrollValues.includes(style.overflow) || scrollValues.includes(style['overflow-y'])}// 获取父层滚动容器function getParentScroller (el) { let parent = el while (parent) { if ([window, document, document.documentElement].includes(parent)) { return window } if (isScroller(parent)) { return parent } parent = parent.parentNode } return parent || window}// 获取容器滚动地位function getScrollTop (el) { return el === window ? window.pageYOffset : el.scrollTop}// 获取容器高度function getOffsetHeight (el) { return el === window ? window.innerHeight : el.offsetHeight}// 滚动到某个地位function scrollToY (el, y) { if (el === window) { window.scroll(0, y) } else { el.scrollTop = y }}// 表格body class名称const TableBodyClassNames = ['.ant-table-scroll .ant-table-body', '.ant-table-fixed-left .ant-table-body-inner', '.ant-table-fixed-right .ant-table-body-inner']let checkOrder = 0 // 多选:记录多选选项扭转的程序export default { inheritAttrs: false, name: 'a-virtual-table', components: { ACheckbox: Checkbox, ATable: Table }, props: { dataSource: { type: Array, default: () => [] }, columns: { type: Array, default: () => [] }, // key值,data数据中的惟一id keyProp: { type: String, default: 'id' }, // 每一行的预估高度 itemSize: { type: Number, default: 60 }, // 指定滚动容器 scrollBox: { type: String }, // 顶部和底部缓冲区域,值越大显示表格的行数越多 buffer: { type: Number, default: 100 }, // 滚动事件的节流工夫 throttleTime: { type: Number, default: 10 }, // 是否获取表格行动静高度 dynamic: { type: Boolean, default: true }, // 是否开启虚构滚动 virtualized: { type: Boolean, default: true }, // 是否是树形构造 isTree: { type: Boolean, default: false } }, data () { return { start: 0, end: undefined, sizes: {}, // 尺寸映射(依赖响应式) renderData: [], // 兼容多选 isCheckedAll: false, // 全选 isCheckedImn: false, // 管制半选款式 isHideAppend: false } }, computed: { tableColumns () { return this.columns.map(column => { // 兼容多选 if (column.type === 'selection') { return { title: () => { return ( <a-checkbox checked={this.isCheckedAll} indeterminate={this.isCheckedImn} onchange={() => this.onCheckAllRows(!this.isCheckedAll)}> </a-checkbox> ) }, customRender: (text, row) => { return ( <a-checkbox checked={row.$v_checked} onchange={() => this.onCheckRow(row, !row.$v_checked)}> </a-checkbox> ) }, width: 60, ...column } } else if (column.index) { // 兼容索引 return { customRender: (text, row, index) => { const curIndex = this.start + index return typeof column.index === 'function' ? column.index(curIndex) : curIndex + 1 }, ...column } } return column }) }, // 计算出每个item(的key值)到滚动容器顶部的间隔 offsetMap ({ keyProp, itemSize, sizes, dataSource }) { if (!this.dynamic) return {} const res = {} let total = 0 for (let i = 0; i < dataSource.length; i++) { const key = dataSource[i][keyProp] res[key] = total const curSize = sizes[key] const size = typeof curSize === 'number' ? curSize : itemSize total += size } return res } }, methods: { // 初始化数据 initData () { // 是否是表格外部滚动 this.isInnerScroll = false this.scroller = this.getScroller() this.setToTop() // 首次须要执行2次handleScroll:因为第一次计算renderData时表格高度未确认导致计算不精确;第二次执行时,表格高度确认后,计算renderData是精确的 this.handleScroll() this.$nextTick(() => { this.handleScroll() }) // 监听事件 this.onScroll = throttle(this.handleScroll, this.throttleTime) this.scroller.addEventListener('scroll', this.onScroll) window.addEventListener('resize', this.onScroll) }, // 设置表格到滚动容器的间隔 setToTop () { if (this.isInnerScroll) { this.toTop = 0 } else { this.toTop = this.$el.getBoundingClientRect().top - (this.scroller === window ? 0 : this.scroller.getBoundingClientRect().top) + getScrollTop(this.scroller) } }, // 获取滚动元素 getScroller () { let el if (this.scrollBox) { if (this.scrollBox === 'window' || this.scrollBox === window) return window el = document.querySelector(this.scrollBox) if (!el) throw new Error(` scrollBox prop: '${this.scrollBox}' is not a valid selector`) if (!isScroller(el)) console.warn(`Warning! scrollBox prop: '${this.scrollBox}' is not a scroll element`) return el } // 如果表格是固定高度,则获取表格内的滚动节点,否则获取父层滚动节点 if (this.$attrs.scroll && this.$attrs.scroll.y) { this.isInnerScroll = true return this.$el.querySelector('.ant-table-body') } else { return getParentScroller(this.$el) } }, // 解决滚动事件 handleScroll () { if (!this.virtualized) return // 更新以后尺寸(高度) this.updateSizes() // 计算renderData this.calcRenderData() // 计算地位 this.calcPosition() }, // 更新尺寸(高度) updateSizes () { if (!this.dynamic) return let rows = [] if (this.isTree) { // 解决树形表格,筛选出一级树形构造 rows = this.$el.querySelectorAll('.ant-table-body .ant-table-row-level-0') } else { rows = this.$el.querySelectorAll('.ant-table-body .ant-table-tbody .ant-table-row') } Array.from(rows).forEach((row, index) => { const item = this.renderData[index] if (!item) return // 计算表格行的高度 let offsetHeight = row.offsetHeight // 表格行如果有扩大行,须要加上扩大内容的高度 const nextEl = row.nextSibling if (nextEl && nextEl.classList && nextEl.classList.contains('ant-table-expanded-row')) { offsetHeight += row.nextSibling.offsetHeight } // 表格行如果有子孙节点,须要加上子孙节点的高度 if (this.isTree) { let next = row.nextSibling while (next && next.tagName === 'TR' && !next.classList.contains('ant-table-row-level-0')) { offsetHeight += next.offsetHeight next = next.nextSibling } } const key = item[this.keyProp] if (this.sizes[key] !== offsetHeight) { this.$set(this.sizes, key, offsetHeight) row._offsetHeight = offsetHeight } }) }, // 计算只在视图上渲染的数据 calcRenderData () { const { scroller, buffer, dataSource: data } = this // 计算可视范畴顶部、底部 const top = getScrollTop(scroller) - buffer - this.toTop const scrollerHeight = this.isInnerScroll ? this.$attrs.scroll.y : getOffsetHeight(scroller) const bottom = getScrollTop(scroller) + scrollerHeight + buffer - this.toTop let start let end if (!this.dynamic) { start = top <= 0 ? 0 : Math.floor(top / this.itemSize) end = bottom <= 0 ? 0 : Math.ceil(bottom / this.itemSize) } else { // 二分法计算可视范畴内的开始的第一个内容 let l = 0 let r = data.length - 1 let mid = 0 while (l <= r) { mid = Math.floor((l + r) / 2) const midVal = this.getItemOffsetTop(mid) if (midVal < top) { const midNextVal = this.getItemOffsetTop(mid + 1) if (midNextVal > top) break l = mid + 1 } else { r = mid - 1 } } // 计算渲染内容的开始、完结索引 start = mid end = data.length - 1 for (let i = start + 1; i < data.length; i++) { const offsetTop = this.getItemOffsetTop(i) if (offsetTop >= bottom) { end = i break } } } // 开始索引始终保持偶数,如果为奇数,则加1使其放弃偶数【确保表格行的偶数数统一,不会导致斑马纹乱序显示】 if (start % 2) { start = start - 1 } this.top = top this.bottom = bottom this.start = start this.end = end this.renderData = data.slice(start, end + 1) this.$emit('change', this.renderData, this.start, this.end) }, // 计算地位 calcPosition () { const last = this.dataSource.length - 1 // 计算内容总高度 const wrapHeight = this.getItemOffsetTop(last) + this.getItemSize(last) // 计算以后滚动地位须要撑起的高度 const offsetTop = this.getItemOffsetTop(this.start) // 设置dom地位 TableBodyClassNames.forEach(className => { const el = this.$el.querySelector(className) if (!el) return // 创立wrapEl、innerEl if (!el.wrapEl) { const wrapEl = document.createElement('div') const innerEl = document.createElement('div') // 此处设置display为'inline-block',是让div宽度等于表格的宽度,修复x轴滚动时左边固定列没有暗影的bug wrapEl.style.display = 'inline-block' innerEl.style.display = 'inline-block' wrapEl.appendChild(innerEl) innerEl.appendChild(el.children[0]) el.insertBefore(wrapEl, el.firstChild) el.wrapEl = wrapEl el.innerEl = innerEl } if (el.wrapEl) { // 设置高度 el.wrapEl.style.height = wrapHeight + 'px' // 设置transform撑起高度 el.innerEl.style.transform = `translateY(${offsetTop}px)` // 设置paddingTop撑起高度 // el.innerEl.style.paddingTop = `${offsetTop}px` } }) }, // 获取某条数据offsetTop getItemOffsetTop (index) { if (!this.dynamic) { return this.itemSize * index } const item = this.dataSource[index] if (item) { return this.offsetMap[item[this.keyProp]] || 0 } return 0 }, // 获取某条数据的尺寸 getItemSize (index) { if (index <= -1) return 0 const item = this.dataSource[index] if (item) { const key = item[this.keyProp] return this.sizes[key] || this.itemSize } return this.itemSize }, // 【内部调用】更新 update () { this.setToTop() this.handleScroll() }, // 【内部调用】滚动到第几行 // (不太准确:滚动到第n行时,如果四周的表格行计算出实在高度后会更新高度,导致内容坍塌或撑起) scrollTo (index, stop = false) { const item = this.dataSource[index] if (item && this.scroller) { this.updateSizes() this.calcRenderData() this.$nextTick(() => { const offsetTop = this.getItemOffsetTop(index) scrollToY(this.scroller, offsetTop) // 调用两次scrollTo,第一次滚动时,如果表格行首次渲染高度发生变化时,会导致滚动地位有偏差,此时须要第二次执行滚动,确保滚动地位无误 if (!stop) { setTimeout(() => { this.scrollTo(index, true) }, 50) } }) } }, // 渲染全副数据 renderAllData () { this.renderData = this.dataSource this.$emit('change', this.dataSource, 0, this.dataSource.length - 1) this.$nextTick(() => { // 革除撑起的高度和地位 TableBodyClassNames.forEach(className => { const el = this.$el.querySelector(className) if (!el) return if (el.wrapEl) { // 设置高度 el.wrapEl.style.height = 'auto' // 设置transform撑起高度 el.innerEl.style.transform = `translateY(${0}px)` } }) }) }, // 执行update办法更新虚构滚动,且每次nextTick只能执行一次【在数据大于100条开启虚构滚动时,因为监听了data、virtualized会间断触发两次update办法:第一次update时,(updateSize)计算尺寸里的渲染数据(renderData)与表格行的dom是一一对应,之后会扭转渲染数据(renderData)的值;而第二次执行update时,renderData扭转了,而表格行dom未扭转,导致renderData与dom不一一对应,从而地位计算错误,最终渲染的数据对应不上。因而应用每次nextTick只能执行一次来防止bug产生】 doUpdate () { if (this.hasDoUpdate) return // nextTick内曾经执行过一次就不执行 if (!this.scroller) return // scroller不存在阐明未初始化实现,不执行 // 启动虚构滚动的霎时,须要临时暗藏el-table__append-wrapper里的内容,不然会导致滚动地位始终到append的内容处 this.isHideAppend = true this.update() this.hasDoUpdate = true this.$nextTick(() => { this.hasDoUpdate = false this.isHideAppend = false }) }, // 兼容多选:抉择表格所有行 onCheckAllRows (val) { val = this.isCheckedImn ? true : val this.dataSource.forEach(row => { if (row.$v_checked === val) return this.$set(row, '$v_checked', val) this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined) }) this.isCheckedAll = val this.isCheckedImn = false this.emitSelectionChange() // 勾销全选,则重置checkOrder if (val === false) checkOrder = 0 }, // 兼容多选:抉择表格某行 onCheckRow (row, val) { if (row.$v_checked === val) return this.$set(row, '$v_checked', val) this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined) const checkedLen = this.dataSource.filter(row => row.$v_checked === true).length if (checkedLen === 0) { this.isCheckedAll = false this.isCheckedImn = false } else if (checkedLen === this.dataSource.length) { this.isCheckedAll = true this.isCheckedImn = false } else { this.isCheckedAll = false this.isCheckedImn = true } this.emitSelectionChange() }, // 多选:兼容表格selection-change事件 emitSelectionChange () { const selection = this.dataSource.filter(row => row.$v_checked).sort((a, b) => a.$v_checkedOrder - b.$v_checkedOrder) this.$emit('selection-change', selection) }, // 多选:兼容表格toggleRowSelection办法 toggleRowSelection (row, selected) { const val = typeof selected === 'boolean' ? selected : !row.$v_checked this.onCheckRow(row, val) }, // 多选:兼容表格clearSelection办法 clearSelection () { this.isCheckedImn = false this.onCheckAllRows(false) } }, watch: { dataSource () { if (!this.virtualized) { this.renderAllData() } else { this.doUpdate() } }, virtualized: { immediate: true, handler (val) { if (!val) { this.renderAllData() } else { this.doUpdate() } } } }, created () { this.$nextTick(() => { this.initData() }) }, mounted () { const appendEl = this.$refs.append this.$el.querySelector('.ant-table-body').appendChild(appendEl) }, beforeDestroy () { if (this.scroller) { this.scroller.removeEventListener('scroll', this.onScroll) window.removeEventListener('resize', this.onScroll) } }}</script><style lang='less'></style>用法<template> <div> <a-virtual-table :columns="columns" :data-source="list" :itemSize="54" keyProp="id" row-key="id" :pagination="false" :scroll="{ x: 1300, y: 800 }"> <a slot="name" slot-scope="{text}">{{ text }}===</a> </a-virtual-table> </div></template><script>import { mockData } from '@/utils'import AVirtualTable from '../a-virtual-table'export default { components: { AVirtualTable }, data () { return { columns: [ { title: 'Name', dataIndex: 'name', key: 'name', scopedSlots: { customRender: 'name' }, fixed: 'left', width: 200 }, { title: 'id', dataIndex: 'id', key: 'id', width: 100 }, { title: 'text', dataIndex: 'text', key: 'text', width: 400 }, { title: 'Address', dataIndex: 'address', key: 'address 1', ellipsis: true, width: 400 }, { title: 'Long Column Long Column Long Column', dataIndex: 'address', key: 'address 2', ellipsis: true, width: 300 }, { title: 'Long Column Long Column', dataIndex: 'address', key: 'address 3', ellipsis: true, width: 300 }, { title: 'Long Column', dataIndex: 'address', key: 'address 4', ellipsis: true, width: 300, fixed: 'right', } ], list: mockData(0, 2000) } }}</script>a-virtual-table 组件Props参数阐明类型可选值默认值dataSource总数据Array必填 keyPropkey值,data数据中的惟一id【⚠️若keyProp未设置或keyProp值不惟一,可能导致表格空数据或者滚动时渲染的数据断层、不连贯】string—iditemSize每一行的预估高度number—60scrollBox指定滚动容器;在指定滚动容器时,如果表格设置了height高度,则滚动容器为表格内的滚动容器;如果表格为设置height高度,则主动获取父层以外的滚动容器,直至window容器为止string—-buffer顶部和底部缓冲区域,值越大显示表格的行数越多Number—200throttleTime滚动事件的节流工夫number—10dynamic动静获取表格行高度,默认开启。设置为false时,则以itemSize为表格行的实在高度,能大大减少虚构滚动计算量,缩小滚动白屏;如果itemSize与表格行的实在高度不统一,可能导致滚动时表格数据错乱boolean—truevirtualized是否开启虚构滚动boolean—true*反对 <a-table> 组件的props属性,更多请看 <a-table> api-—-Methods办法名阐明参数scrollTo滚动到第几行【不太准确:因为滚动到第n行时,如果四周的表格行计算出实在高度后会更新高度,导致以后行坍塌或撑起】indexupdate更新-clearSelection用于多选 <virtual-column type="selection">,清空用户的抉择-toggleRowSelection用于多选 <virtual-column type="selection">, 切换某一行的选中状态,如果应用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)row, selectedEvents事件名称阐明参数change计算实现实在显示的表格行数(renderData, start, end):renderData 实在渲染的数据,start和end指的是渲染的数据在总数据的开始到完结的区间范畴selection-change虚构表格多选选项产生更改时触发事件selectedRows

January 13, 2023 · 8 min · jiezi

关于vue.js:Vue32-Ref操作Dom为何既易用又高效

缘起在开发一个我的项目之前,咱们往往都是先做下需要剖析,针对前端而言,就是能够调研或者抉择一个根底组件库,来进步咱们的工作效率。毕竟,对比拟计较工夫老本的公司来说,不会给你拿看电视剧玩游戏的工夫去专门开发一个相似日历的组件。但在市面上的组件库,并不能都能满足咱们的需要。这个时候,咱们就须要本人手写组件,来利用到我的项目中。 而这就是我想说的: 如何设计组件,让其既能易于利用(或者说缩小代码量),又能进步扩展性,不便需要变更和后续保护? 能够有很多种形式,而利用Ref操作Dom的个性随是其中之一,但这个形式却让咱们在保护和操作Modal、Popup以及频繁操作Dom显示和暗藏交互的组件的时候,却施展得很大劣势。就对Ref操作Dom的相干知识点以及利用实例分几个方面来做下分析 Ref获取Dom的实质Ref操作Dom在Vue2.x和Vue3.x的不同Ref操作组件Dom和父子组件单向传递比照详说Ref获取Dom的实质Vue2.x中Vue的对象属性$refs,其实就是所有注册过的ref的一个汇合,而ref对应着template模版中,不同组件或一般Dom元素上关联的ref="xx"; 源码中ref的理论获取形式也是通过原生形式getElementById而失去的Dom节点;能够说ref是document.getElementById的语法糖。vue3的ref连续了vue2的用法,还减少了一个作用就是创立响应式数据 兴许有人会问了,既然ref和getElementById都能获取到Dom,那么在我的项目开发中,我抉择哪种形式都没什么区别呢?对于这个问题,通过数据表明,$refs绝对document.getElementById的办法,会缩小获取dom节点的耗费;而具体起因,等下一篇文章再具体探讨。 Ref操作Dom在Vue2.x和Vue3.x的不同Vue2.x咱们只须要在相应的Dom元素或者组件加上ref="xx"属性,而后在Vue对象中应用this.$refs.xx,就能够间接获取到该Dom并操作其办法属性, <user-and-dep-tree-select-modal ref="avaUserTreeSelect" title="選擇可見範圍" :project-id="currentProjectId" :visible.sync="avaUserModalVisible" @ok="editAvailableUser"/>或者<div class="user" ref="user">dd</div>// $refsshowManagerModal () { this.$refs.avaUserTreeSelect.showModal(this.form.managers) console.log(this.$refs.user.text)},Vue3.2在Vue3.2版本应用的形式 //一般Dom<div class="user" ref="user"></div>//组件<batch-adjust-department-modal ref="batchAdjustDepartmentRef" /><script setup lang="ts">import { ref } from 'vue';// modal调整部门弹层Domconst batchAdjustDepartmentRef = ref(null);const user = ref(null);</script>兴许这里有人疑难,为什么申明了一个和template的ref中同名的常量变量就绑定了对应的dom?在这里再补充阐明一下: Vue3 在晚期版本( 3.0.0-beta.21 之前)中对 composition api 的反对,只能在组件选项 setup 函数中应用。而相应式的变量都是通过在setup()办法中return {写入须要在模版中应用的变量或办法} <script>import { defineComponent, ref } from 'vue'export default defineComponent({ name: 'HelloWorld', setup(props, ctx) {const count = ref(0)function add() { count.value++}// 应用return {} 把变量、办法裸露给模板return { count, add,} },})</script>在 3.0.0-beta.21 版本中减少了 <script setup> 的试验个性。如果应用了,会提醒 <script setup> 还处在试验个性阶段。在 3.2.0 版本中移除 <script setup> 的试验状态,从此,宣告 <script setup> 正式转正应用,成为框架稳固的个性之一与组件选项 setup 函数比照, <script setup> 咱们只须要写更少、更简洁的代码,不须要应用 return {} 裸露变量和办法了,应用组件时不须要被动注册了,会主动帮你绑定所以在<script setup>中申明的变量会主动被加到该Vue对象的自身this中,如 ...

January 13, 2023 · 2 min · jiezi

关于vue.js:手把手搭建-VueTSExpress-全栈-SSR-博客系统前端起步篇

前端我的项目搭建前后端全栈博客我的项目 By huangyan321在线体验:https://docsv3.hgyn23.cn 目录构造( project/client ).|-- build //构建相干|-- cache //全局缓存|-- public //公共动态文件`-- src |-- @types //类型定义 |-- App.vue //主页面 |-- api //接口文件 |-- assets //资产文件夹 | |-- fonts //字体文件 | |-- img //图片文件 | `-- svg //svg文件 |-- common //通用组件 |-- components //插件注册 |-- config //配置 |-- entry.client.ts //客户端入口 |-- entry.server.ts //服务端入口 |-- enums //枚举 |-- hooks //封装的hooks |-- layout //布局 |-- main.ts //主入口 |-- router //路由 |-- service //网络申请 |-- store //全局存储 |-- styles //款式 |-- utils //工具 `-- views //页面上面跟我一起来站在一个开发的角度从零开始构建一个残缺的Vue-SSR前端我的项目吧~ ...

January 9, 2023 · 9 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。 ...

January 9, 2023 · 2 min · jiezi

关于vue.js:百度前端经典vue面试题整理

子组件能够间接扭转父组件的数据吗?子组件不能够间接扭转父组件的数据。这样做次要是为了保护父子组件的单向数据流。每次父级组件产生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中收回正告。 Vue提倡单向数据流,即父级 props 的更新会流向子组件,然而反过来则不行。这是为了避免意外的扭转父组件状态,使得利用的数据流变得难以了解,导致数据流凌乱。如果毁坏了单向数据流,当利用简单时,debug 的老本会十分高。 只能通过 $emit 派发一个自定义事件,父组件接管到后,由父组件批改。 你有对 Vue 我的项目进行哪些优化?(1)代码层面的优化 v-if 和 v-show 辨别应用场景computed 和 watch 辨别应用场景v-for 遍历必须为 item 增加 key,且防止同时应用 v-if长列表性能优化事件的销毁图片资源懒加载路由懒加载第三方插件的按需引入优化有限列表性能服务端渲染 SSR or 预渲染(2)Webpack 层面的优化 Webpack 对图片进行压缩缩小 ES6 转为 ES5 的冗余代码提取公共代码模板预编译提取组件的 CSS优化 SourceMap构建后果输入剖析Vue 我的项目的编译优化(3)根底的 Web 技术的优化 开启 gzip 压缩浏览器缓存CDN 的应用应用 Chrome Performance 查找性能瓶颈Vue.js的template编译简而言之,就是先转化成AST树,再失去的render函数返回VNode(Vue的虚构DOM节点),具体步骤如下: 首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的形象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创立编译器的。另外compile还负责合并option。 而后,AST会通过generate(将AST语法树转化成render funtion字符串的过程)失去render函数,render的返回值是VNode,VNode是Vue的虚构DOM节点,外面有(标签名、子节点、文本等等) 对虚构DOM的了解?从实质上来说,Virtual Dom是一个JavaScript对象,通过对象的形式来示意DOM构造。将页面的状态形象为JS对象的模式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将屡次DOM批改的后果一次性的更新到页面上,从而无效的缩小页面渲染的次数,缩小批改DOM的重绘重排次数,进步渲染性能。 虚构DOM是对DOM的形象,这个对象是更加轻量级的对 DOM的形容。它设计的最后目标,就是更好的跨平台,比方Node.js就没有DOM,如果想实现SSR,那么一个形式就是借助虚构DOM,因为虚构DOM自身是js对象。 在代码渲染到页面之前,vue会把代码转换成一个对象(虚构 DOM)。以对象的模式来形容实在DOM构造,最终渲染到页面。在每次数据发生变化前,虚构DOM都会缓存一份,变动之时,当初的虚构DOM会与缓存的虚构DOM进行比拟。在vue外部封装了diff算法,通过这个算法来进行比拟,渲染时批改扭转的变动,原先没有产生扭转的通过原先的数据进行渲染。 另外古代前端框架的一个根本要求就是毋庸手动操作DOM,一方面是因为手动操作DOM无奈保障程序性能,多人合作的我的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作能够大大提高开发效率。 Vue template 到 render 的过程vue的模版编译过程次要如下:template -> ast -> render函数 ...

January 9, 2023 · 4 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监听对象属性的变动,那么问题来了,咱们常常在传递数据的时候往往不是一个对象,很有可能是一个数组,那是不是就没有方法了呢,答案显然是否则的。那么上面就看看作者是如何监听数组的变动: ...

January 9, 2023 · 4 min · jiezi

关于vue.js:前端二面经典vue面试题指南

v-model 的原理?咱们在 vue 我的项目中次要应用 v-model 指令在表单 input、textarea、select 等元素上创立双向数据绑定,咱们晓得 v-model 实质上不过是语法糖,v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件: text 和 textarea 元素应用 value 属性和 input 事件;checkbox 和 radio 应用 checked 属性和 change 事件;select 字段将 value 作为 prop 并将 change 作为事件。以 input 表单元素为例: <input v-model='something'>相当于<input v-bind:value="something" v-on:input="something = $event.target.value">如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示: 父组件:<ModelChild v-model="message"></ModelChild>子组件:<div>{{value}}</div>props:{ value: String},methods: { test1(){ this.$emit('input', '小红') },},Vue.extend 作用和原理官网解释:Vue.extend 应用根底 Vue 结构器,创立一个“子类”。参数是一个蕴含组件选项的对象。其实就是一个子类结构器 是 Vue 组件的外围 api 实现思路就是应用原型继承的办法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并 ...

January 9, 2023 · 4 min · jiezi

关于vue.js:2023前端二面必会vue面试题指南

action 与 mutation 的区别 mutation 是同步更新, $watch 严格模式下会报错 action 是异步操作,能够获取数据后调用 mutation 提交最终数据Vue路由hash模式和history模式1. hash模式 晚期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简略,location.hash 的值就是 URL中 # 前面的内容。比方上面这个网站,它的 location.hash 的值为 '#search' https://interview2.poetries.top#searchhash 路由模式的实现次要是基于上面几个个性 URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 局部不会被发送;hash 值的扭转,都会在浏览器的拜访历史中减少一个记录。因而咱们能通过浏览器的回退、后退按钮管制 hash 的切换;能够通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会产生扭转;或者应用 JavaScript 来对 loaction.hash 进行赋值,扭转 URL 的 hash 值;咱们能够应用 hashchange 事件来监听 hash 值的变动,从而对页面进行跳转(渲染)window.addEventListener("hashchange", funcRef, false);每一次扭转 hash(window.location.hash),都会在浏览器的拜访历史中减少一个记录利用 hash 的以上特点,就能够来实现前端路由“更新视图但不从新申请页面”的性能了 特点 :兼容性好然而不美观 2. history模式 history采纳HTML5的新个性;且提供了两个新办法: pushState(), replaceState()能够对浏览器历史记录栈进行批改,以及popState事件的监听到状态变更 window.history.pushState(null, null, path);window.history.replaceState(null, null, path);这两个办法有个独特的特点:当调用他们批改浏览器历史记录栈后,尽管以后 URL 扭转了,但浏览器不会刷新页面,这就为单页利用前端路由“更新视图但不从新申请页面”提供了根底。 ...

January 6, 2023 · 13 min · jiezi

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

背景看了一些自定义指令的文章,然而探索其原理的文章却不多见,所以我决定水一篇。 如何自定义指令?其实对于这个问题官网文档上曾经有了很好的示例的,咱们先来温故一下。 除了外围性能默认内置的指令 (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 来进行。 ...

January 6, 2023 · 4 min · jiezi

关于vue.js:字节前端必会vue面试题集锦

Vue3有理解过吗?能说说跟vue2的区别吗?1. 哪些变动 从上图中,咱们能够概览Vue3的新个性,如下: 速度更快体积缩小更易保护更靠近原生更易使用1.1 速度更快 vue3相比vue2 重写了虚构Dom实现编译模板的优化更高效的组件初始化undate性能进步1.3~2倍SSR速度进步了2~3倍 1.2 体积更小 通过webpack的tree-shaking性能,能够将无用模块“剪辑”,仅打包须要的 可能tree-shaking,有两大益处: 对开发人员,可能对vue实现更多其余的性能,而不用担心整体体积过大对使用者,打包进去的包体积变小了vue能够开发出更多其余的性能,而不用担心vue打包进去的整体体积过多 1.3 更易保护 compositon Api 可与现有的Options API一起应用灵便的逻辑组合与复用Vue3模块能够和其余框架搭配应用 更好的Typescript反对 VUE3是基于typescipt编写的,能够享受到主动的类型定义提醒 1.4 编译器重写 1.5 更靠近原生 能够自定义渲染 API 1.6 更易使用 响应式 Api 裸露进去 轻松辨认组件从新渲染起因 2. Vue3新增个性 Vue 3 中须要关注的一些新性能包含: framentsTeleportcomposition ApicreateRenderer2.1 framents 在 Vue3.x 中,组件当初反对有多个根节点 <!-- Layout.vue --><template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer></template>2.2 Teleport Teleport 是一种可能将咱们的模板挪动到 DOM 中 Vue app 之外的其余地位的技术,就有点像哆啦A梦的“任意门” 在vue2中,像 modals,toast 等这样的元素,如果咱们嵌套在 Vue 的某个组件外部,那么解决嵌套组件的定位、z-index 和款式就会变得很艰难 ...

January 6, 2023 · 10 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 更新操作 ...

January 6, 2023 · 2 min · jiezi

关于vue.js:前端高频vue面试题总结

created和mounted的区别created:在模板渲染成html前调用,即通常初始化某些属性值,而后再渲染成视图。mounted:在模板渲染成html后调用,通常是初始化页面实现后,再对html的dom节点进行一些须要的操作。如何从实在DOM到虚构DOM波及到Vue中的模板编译原理,次要过程: 将模板转换成 ast 树, ast 用对象来形容实在的JS语法(将实在DOM转换成虚构DOM)优化树将 ast 树生成代码Vue3有理解过吗?能说说跟vue2的区别吗?1. 哪些变动 从上图中,咱们能够概览Vue3的新个性,如下: 速度更快体积缩小更易保护更靠近原生更易使用1.1 速度更快 vue3相比vue2 重写了虚构Dom实现编译模板的优化更高效的组件初始化undate性能进步1.3~2倍SSR速度进步了2~3倍 1.2 体积更小 通过webpack的tree-shaking性能,能够将无用模块“剪辑”,仅打包须要的 可能tree-shaking,有两大益处: 对开发人员,可能对vue实现更多其余的性能,而不用担心整体体积过大对使用者,打包进去的包体积变小了vue能够开发出更多其余的性能,而不用担心vue打包进去的整体体积过多 1.3 更易保护 compositon Api 可与现有的Options API一起应用灵便的逻辑组合与复用Vue3模块能够和其余框架搭配应用 更好的Typescript反对 VUE3是基于typescipt编写的,能够享受到主动的类型定义提醒 1.4 编译器重写 1.5 更靠近原生 能够自定义渲染 API 1.6 更易使用 响应式 Api 裸露进去 轻松辨认组件从新渲染起因 2. Vue3新增个性 Vue 3 中须要关注的一些新性能包含: framentsTeleportcomposition ApicreateRenderer2.1 framents 在 Vue3.x 中,组件当初反对有多个根节点 <!-- Layout.vue --><template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer></template>2.2 Teleport Teleport 是一种可能将咱们的模板挪动到 DOM 中 Vue app 之外的其余地位的技术,就有点像哆啦A梦的“任意门” ...

January 5, 2023 · 8 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。 ...

January 5, 2023 · 5 min · jiezi

关于vue.js:校招前端一面必会vue面试题指南

写过自定义指令吗?原理是什么答复范例 Vue有一组默认指令,比方v-model或v-for,同时Vue也容许用户注册自定义指令来扩大Vue能力自定义指令次要实现一些可复用低层级DOM操作应用自定义指令分为定义、注册和应用三步:定义自定义指令有两种形式:对象和函数模式,前者相似组件定义,有各种生命周期;后者只会在mounted和updated时执行注册自定义指令相似组件,能够应用app.directive()全局注册,应用{directives:{xxx}}部分注册应用时在注册名称前加上v-即可,比方v-focus我在我的项目中罕用到一些自定义指令,例如:复制粘贴 v-copy长按 v-longpress防抖 v-debounce图片懒加载 v-lazy按钮权限 v-premission页面水印 v-waterMarker拖拽指令 v-draggablevue3中指令定义产生了比拟大的变动,次要是钩子的名称放弃和组件统一,这样开发人员容易记忆,不易犯错。另外在v3.2之后,能够在setup中以一个小写v结尾不便的定义自定义指令,更简略了根本应用当Vue中的外围内置指令不可能满足咱们的需要时,咱们能够定制自定义的指令用来满足开发的需要咱们看到的v-结尾的行内属性,都是指令,不同的指令能够实现或实现不同的性能,对一般 DOM元素进行底层操作,这时候就会用到自定义指令。除了外围性能默认内置的指令 (v-model 和 v-show),Vue 也容许注册自定义指令 // 指令应用的几种形式://会实例化一个指令,但这个指令没有参数 `v-xxx`// -- 将值传到指令中`v-xxx="value"` // -- 将字符串传入到指令中,如`v-html="'<p>内容</p>'"``v-xxx="'string'"` // -- 传参数(`arg`),如`v-bind:class="className"``v-xxx:arg="value"` // -- 应用修饰符(`modifier`)`v-xxx:arg.modifier="value"` 注册一个自定义指令有全局注册与部分注册 // 全局注册注册次要是用过Vue.directive办法进行注册// Vue.directive第一个参数是指令的名字(不须要写上v-前缀),第二个参数能够是对象数据,也能够是一个指令函数// 注册一个全局自定义指令 `v-focus`Vue.directive('focus', { // 当被绑定的元素插入到 DOM 中时…… inserted: function (el) { // 聚焦元素 el.focus() // 页面加载实现之后主动让输入框获取到焦点的小性能 }})// 部分注册通过在组件options选项中设置directive属性directives: { focus: { // 指令的定义 inserted: function (el) { el.focus() // 页面加载实现之后主动让输入框获取到焦点的小性能 } }}// 而后你能够在模板中任何元素上应用新的 v-focus property,如下:<input v-focus />钩子函数 ...

January 5, 2023 · 6 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>这样就只判断一次就能达到渲染成果了,是不是更好一些那参考 前端进阶面试题具体解答 ...

January 5, 2023 · 3 min · jiezi

关于vue.js:腾讯前端vue面试题合集

能说下 vue-router 中罕用的 hash 和 history 路由模式实现原理吗?(1)hash 模式的实现原理 晚期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简略,location.hash 的值就是 URL 中 # 前面的内容。比方上面这个网站,它的 location.hash 的值为 '#search': https://www.word.com#searchhash 路由模式的实现次要是基于上面几个个性: URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 局部不会被发送;hash 值的扭转,都会在浏览器的拜访历史中减少一个记录。因而咱们能通过浏览器的回退、后退按钮管制hash 的切换; 能够通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会产生扭转;或者应用 JavaScript 来对 loaction.hash 进行赋值,扭转 URL 的 hash 值;咱们能够应用 hashchange 事件来监听 hash 值的变动,从而对页面进行跳转(渲染)。(2)history 模式的实现原理 HTML5 提供了 History API 来实现 URL 的变动。其中做最次要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 能够在不进行刷新的状况下,操作浏览器的历史纪录。惟一不同的是,前者是新增一个历史记录,后者是间接替换以后的历史记录,如下所示: window.history.pushState(null, null, path);window.history.replaceState(null, null, path);history 路由模式的实现次要基于存在上面几个个性: pushState 和 repalceState 两个 API 来操作实现 URL 的变动 ;咱们能够应用 popstate 事件来监听 url 的变动,从而对页面进行跳转(渲染);history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时咱们须要手动触发页面跳转(渲染)。v-show 与 v-if 有什么区别?v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 ...

January 4, 2023 · 7 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 监听数据读取和设置数据描述符绑定实现后,咱们就能失去以下的流程图 图中咱们能够看出,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 的响应式原理 ...

January 4, 2023 · 6 min · jiezi

关于vue.js:腾讯前端一面常考vue面试题汇总

vue2.x具体1. 剖析 首先找到vue的构造函数 源码地位:src\core\instance\index.js 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') } this._init(options)}options是用户传递过去的配置项,如data、methods等罕用的办法 vue构建函数调用_init办法,但咱们发现本文件中并没有此办法,但认真能够看到文件下方定定义了很多初始化办法 initMixin(Vue); // 定义 _initstateMixin(Vue); // 定义 $set $get $delete $watch 等eventsMixin(Vue); // 定义事件 $on $once $off $emitlifecycleMixin(Vue);// 定义 _update $forceUpdate $destroyrenderMixin(Vue); // 定义 _render 返回虚构dom首先能够看initMixin办法,发现该办法在Vue原型上定义了_init办法 源码地位:src\core\instance\init.js Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options // 合并属性,判断初始化的是否是组件,这里合并次要是 mixins 或 extends 的办法 if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // 合并vue属性 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { // 初始化proxy拦截器 initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm // 初始化组件生命周期标记位 initLifecycle(vm) // 初始化组件事件侦听 initEvents(vm) // 初始化渲染办法 initRender(vm) callHook(vm, 'beforeCreate') // 初始化依赖注入内容,在初始化data、props之前 initInjections(vm) // resolve injections before data/props // 初始化props/data/method/watch/methods initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } // 挂载元素 if (vm.$options.el) { vm.$mount(vm.$options.el) } }仔细阅读下面的代码,咱们失去以下论断: ...

January 4, 2023 · 20 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参数,开发环境下会给予开发者一个正告,在这种状况,为了不影响生产环境,生产环境下会将正告放过。 ...

January 4, 2023 · 4 min · jiezi

关于vue.js:vue3个人心得功能详解七父传子-props

父传子 props子能够通过defineExpose({})裸露要向父传递的变量、函数、DOM,父通过给子组件加ref属性,通过"ref值.value.子变量"语句来操作子传过来的变量、函数、DOM,且父子间的值是双向绑定的。那么父怎么向子传递变量、函数、DOM:1、统统放在子组件标签里把要传给子的变量、函数、DOM或字符数字等,以子组件的属性模式放在标签里。如下: <script setup>import HelloWor from './HelloWor.vue'import {ref} from 'vue'const wowo=ref('我是子组件的ref属性')const wo=ref('我是变量')function iio(){console.log('我是函数')}const wow=ref('我是DOM')</script><template> <button ref="wow" >草</button> <HelloWor ref="wowo" :ioo="wo" :idd="iio" :oj="wow" id="我是原生属性" ipp="我是字符" ip="8"/></template>留神:传递变量、函数、DOM时,要带响应绑定指令(v-bind)的模式。如是传递标签的原生属性或字符时,不须要v-bind指令.2、在子组件内用defineProps([])先注册子组件应用父传递的值前须要先用defineProps([])注册,如下: <script setup>import {ref} from 'vue'const wee=defineProps(['ref','ioo','idd','oj','id','ipp','ip'])function wocao(){console.log(wee.ref,wee.ioo,wee.idd(),wee.oj,wee.id,wee.ipp,wee.ip)}</script><template> <button @click="wocao">测试</button> <div >{{ref}},{{ioo}},{{idd}},{{oj}},{{id}},{{ipp}},{{ip}}</div></template>留神写法:所要传进来的值放在[]中括号里.且值要加引号.3、模板部与逻辑部的不同用法在模板部间接应用defineProps后的值就行逻辑部不能间接应用defineProps后的值,需将defineProps赋值给一个变量,传进来的值以这个变量的属性的模式来应用.4、单向绑定与可读因子对父传过来的值只可读不可写,所以是单向绑定,子响应父的扭转,子无权扭转传过来的值.如上,子对wee.ioo赋值会跳出wee.ioo非可写正告.5、函数的应用要加()括号defineProps后的函数类值,应用时要加()号,不能的话只是个函数款式,不会运行函数,如上idd函数不加()不会运行函数. 2B的emit监听事件今天再写

January 3, 2023 · 1 min · jiezi

关于vue.js:前端二面vue面试题边面边更

Vuex有哪几种属性?有五种,别离是 State、 Getter、Mutation 、Action、 Module state => 根本数据(数据源寄存地)getters => 从根本数据派生进去的数据mutations => 提交更改数据的办法,同步actions => 像一个装璜器,包裹mutations,使之能够异步。modules => 模块化Vuex谈谈对keep-alive的理解keep-alive 能够实现组件的缓存,当组件切换时不会对以后组件进行卸载。罕用的2个属性 include/exclude ,2个生命周期 activated , deactivated vue2.x具体1. 剖析 首先找到vue的构造函数 源码地位:src\core\instance\index.js 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') } this._init(options)}options是用户传递过去的配置项,如data、methods等罕用的办法 vue构建函数调用_init办法,但咱们发现本文件中并没有此办法,但认真能够看到文件下方定定义了很多初始化办法 initMixin(Vue); // 定义 _initstateMixin(Vue); // 定义 $set $get $delete $watch 等eventsMixin(Vue); // 定义事件 $on $once $off $emitlifecycleMixin(Vue);// 定义 _update $forceUpdate $destroyrenderMixin(Vue); // 定义 _render 返回虚构dom首先能够看initMixin办法,发现该办法在Vue原型上定义了_init办法 ...

January 3, 2023 · 14 min · jiezi

关于vue.js:vue组件通信6种方式总结常问知识点

前言在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/@ 进行监听。 子组件 ComponentA: <template> <div> <component-b :title="title" @title-change="titleChange"></component-b> </div></template><script>import ComponentB from './ComponentB'export default { name: 'ComponentA', components: { ComponentB }, data: { title: 'Click me' }, methods: { titleChange(newTitle) { this.title = newTitle } }}</script>子组件 ComponentB: ...

January 3, 2023 · 5 min · jiezi

关于vue.js:滴滴前端一面高频vue面试题及答案

keep-alive 应用场景和原理keep-alive 是 Vue 内置的一个组件, 能够实现组件缓存 ,当组件切换时不会对以后组件进行卸载。 个别联合路由和动静组件一起应用 ,用于缓存组件提供 include 和 exclude 属性, 容许组件有条件的进行缓存 。两者都反对字符串或正则表达式,include 示意只有名称匹配的组件会被缓存,exclude 示意任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高对应两个钩子函数 activated 和deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivatedkeep-alive 的中还使用了 LRU(最近起码应用) 算法,抉择最近最久未应用的组件予以淘汰<keep-alive></keep-alive> 包裹动静组件时,会缓存不流动的组件实例,次要用于保留组件状态或防止从新渲染比方有一个列表和一个详情,那么用户就会常常执行关上详情=>返回列表=>关上详情…这样的话列表和详情都是一个频率很高的页面,那么就能够对列表组件应用<keep-alive></keep-alive>进行缓存,这样用户每次返回列表的时候,都能从缓存中疾速渲染,而不是从新渲染对于keep-alive的根本用法 <keep-alive> <component :is="view"></component></keep-alive>应用includes和exclude: <keep-alive include="a,b"> <component :is="view"></component></keep-alive><!-- 正则表达式 (应用 `v-bind`) --><keep-alive :include="/a|b/"> <component :is="view"></component></keep-alive><!-- 数组 (应用 `v-bind`) --><keep-alive :include="['a', 'b']"> <component :is="view"></component></keep-alive>匹配首先查看组件本身的 name 选项,如果 name 选项不可用,则匹配它的部分注册名称 (父组件 components 选项的键值),匿名组件不能被匹配 设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated与deactivated): 首次进入组件时:beforeRouteEnter > beforeCreate > created> mounted > activated > ... ... > beforeRouteLeave > deactivated再次进入组件时:beforeRouteEnter >activated > ... ... > beforeRouteLeave > deactivated应用场景 ...

January 3, 2023 · 8 min · jiezi

关于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 (这种办法理论批改的是本人的数据 父组件的数据没变) ...

January 3, 2023 · 4 min · jiezi

关于vue.js:Vue学习笔记六-长乐未央

例子依然来自Mdn Web Docs,加上了我本人的了解。长乐未央,长毋相忘。某种意义上算是MDN web docs 中Vue教程的翻译,但又加上了本人的了解。 地址是: https://developer.mozilla.org... 进化: 编辑组件当初咱们的组件依然不太完满,因为它不能编辑,输错了,就输错了。对此咱们的解决方案是引入一个编辑组件。还是固定的步骤,首先在src/components下建设一个文件,咱们将其命名为ToDoItemEditForm.vue。而后将上面你的代码复制到这个文件中: <template> <form class="stack-small" @submit.prevent="onSubmit"> <div> <label class="edit-label">Edit Name for &quot;{{label}}&quot;</label> <input :id="id" type="text" autocomplete="off" v-model.lazy.trim="newLabel" /> </div> <div class="btn-group"> <button type="button" class="btn" @click="onCancel"> 勾销 <span class="visually-hidden">正在编辑 {{label}}</span> </button> <button type="submit" class="btn btn__primary"> 保留 <span class="visually-hidden">对{{label}}进行批改</span> </button> </div> </form></template><script> export default { props: { label: { type: String, required: true, }, id: { type: String, required: true, }, }, data() { return { newLabel: this.label, }; }, methods: { onSubmit() { if (this.newLabel && this.newLabel !== this.label) { this.$emit("item-edited", this.newLabel); } }, onCancel() { this.$emit("edit-cancelled"); }, }, };</script><style scoped> .edit-label { font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #0b0c0c; display: block; margin-bottom: 5px; } input { display: inline-block; margin-top: 0.4rem; width: 100%; min-height: 4.4rem; padding: 0.4rem 0.8rem; border: 2px solid #565656; } form { display: flex; flex-direction: row; flex-wrap: wrap; } form > * { flex: 0 0 100%; }</style>咱们大抵的解读一下这个文件,在这个文件外面咱们创立了一个表单,表单中的input用于编辑待办事项的名称,用v-model和data中的newLabel建设了双向绑定。同时咱们申明了这个组件接管的音讯(变量)prop。 ...

January 2, 2023 · 4 min · jiezi

关于vue.js:2023前端二面高频vue面试题集锦

vuex是什么?怎么应用?哪种性能场景应用它?Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源寄存地,对应于个别 vue 对象外面的 data 外面寄存的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据产生扭转,依赖这相数据的组件也会产生更新它通过 mapState 把全局的 state 和 getters 映射到以后组件的 computed 计算属性vuex 个别用于中大型 web 单页利用中对利用的状态进行治理,对于一些组件间关系较为简单的小型利用,应用 vuex 的必要性不是很大,因为齐全能够用组件 prop 属性或者事件来实现父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据。应用Vuex解决非父子组件之间通信问题 vuex 是通过将 state 作为数据中心、各个组件共享 state 实现跨组件通信的,此时的数据齐全独立于组件,因而将组件间共享的数据置于 State 中能无效解决多层级组件嵌套的跨组件通信问题vuex 的 State 在单页利用的开发中自身具备一个“数据库”的作用,能够将组件中用到的数据存储在 State 中,并在 Action 中封装数据读写的逻辑。这时候存在一个问题,个别什么样的数据会放在 State 中呢? 目前次要有两种数据会应用 vuex 进行治理:组件之间全局共享的数据通过后端异步申请的数据 包含以下几个模块 state:Vuex 应用繁多状态树,即每个利用将仅仅蕴含一个store 实例。外面寄存的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据产生扭转,依赖这相数据的组件也会产生更新。它通过 mapState 把全局的 state 和 getters 映射到以后组件的 computed 计算属性mutations:更改Vuex的store中的状态的惟一办法是提交mutationgetters:getter 能够对 state 进行计算操作,它就是 store 的计算属性尽管在组件内也能够做计算属性,然而 getters 能够在多给件之间复用如果一个状态只在一个组件内应用,是能够不必 gettersaction:action 相似于 muation, 不同在于:action 提交的是 mutation,而不是间接变更状态action 能够蕴含任意异步操作modules:面对简单的应用程序,当治理的状态比拟多时;咱们须要将vuex的store对象宰割成模块(modules) ...

January 2, 2023 · 11 min · jiezi

关于vue.js:new-Vue的时候到底做了什么

Vue加载流程1.初始化的第一阶段是Vue实例也就是vm对象创立前后:首先Vue进行生命周期,事件初始化产生在beforeCreate生命周期函数前,而后进行数据监测和数据代理的初始化,也就是创立vm对象的过程,当vm对象创立实现就能够通过vm对象拜访到劫持的数据,比方data中的数据,methods中的办法等。而后Vue调用外部的render函数开始解析模板将其解析为一个JS对象也即在内存中生成虚构DOM也就是Vnode对象。第二阶段是vm对象挂载前后:挂载实现前页面出现的是未通过Vue编译的DOM构造,所有对DOM的操作最终都不会失效。挂载前首先将内存中的Vnode转换为实在DOM插入页面,此时实现挂载。页面中出现的就是通过Vue编译的DOM构造,至此初始化过程完结。 2.开启订阅音讯也就是数据劫持代理监听,其实就是写了一个watcher函数去监听数据的扭转,发送网络申请,绑定自定义事件等初始化操作。当数据发生变化当前即状态变更的时候,会从新结构新的Vnode对象。而后用新的Vnode对象和旧的Vnode对象进行差别比拟也就是DIFF算法,而后把差别利用到旧的Vnode对象所构建的真正的DOM树上这个过程就是patch,视图就更新了 每一个组件在加载时都会调用Vue外部的render函数把该组件的tamplate选项的模板解析为一个JS对象,这个对象和DOM节点对象构造一样,而后是数据劫持代理监听,当数据发生变化当前,将旧Vnode对象和生成的新Vnode对象比拟差别而后更新DOM Vnode: {tag:"", id:, name:"Box", $el:实在页面上的DOM的援用, //等等属性 chiren:[ { tag:"", id:, name:"Box2",$el:实在页面上的DOM的援用, //等等属性 }, { tag:"", id:, name:"Box3",$el:实在页面上的DOM的援用,//等等属性 }] } 什么是DIFFdiff算法是一种比照算法。比照两者是旧虚构DOM和新虚构DOM,比照出是哪个虚构节点更改了,找出这个虚构节点,并只更新这个虚构节点所对应的实在节点,而不必更新其余数据没产生扭转的节点,实现精准地更新实在DOM,进而提高效率 其有两个特点: 比拟只会在同层级进行, 不会跨层级比拟在diff比拟的过程中,循环从两边向两头比拟 DIFF算法的过程: 当数据产生扭转时,订阅者watcher就会调用patch给实在的DOM打补丁通过isSameVnode进行判断,雷同则调用patchVnode办法patchVnode做了以下操作: 找到对应的实在dom,称为el如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点如果oldVnode有子节点而VNode没有,则删除el子节点如果oldVnode没有子节点而VNode有,则将VNode的子节点实在化后增加到el如果两者都有子节点,则执行updateChildren函数比拟子节点updateChildren次要做了以下操作: 设置新旧VNode的头尾指针新旧头尾指针进行比拟,循环向两头聚拢,依据状况调用patchVnode进行patch反复流程、调用createElem创立一个新节点,从哈希表寻找 key统一的VNode 节点再分状况操作 参考 前端进阶面试题具体解答 对于Vue中el,template,render,$mount的渲染渲染根节点: 先判断有无el属性,有的话间接获取el根节点,没有的话调用$mount去获取根节点。渲染模板: 有render:这时候优先执行render函数,render优先级 > template。无render:有template时拿template去解析成render函数的所需的格局,并应用调用render函数渲染。无template时拿el根节点的outerHTML去解析成render函数的所需的格局,并应用调用render函数渲染渲染的形式:无论什么状况,最初都对立是要应用render函数渲染

January 2, 2023 · 1 min · jiezi

关于vue.js:校招前端二面高频vue面试题

vue-router中如何爱护路由剖析 路由爱护在利用开发过程中十分重要,简直每个利用都要做各种路由权限治理,因而相当考查使用者基本功。 体验 全局守卫: const router = createRouter({ ... })router.beforeEach((to, from) => { // ... // 返回 false 以勾销导航 return false})路由独享守卫: const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { // reject the navigation return false }, },]组件内的守卫: const UserDetails = { template: `...`, beforeRouteEnter(to, from) { // 在渲染该组件的对应路由被验证前调用 }, beforeRouteUpdate(to, from) { // 在以后路由扭转,然而该组件被复用时调用 }, beforeRouteLeave(to, from) { // 在导航来到渲染该组件的对应路由时调用 },}答复 vue-router中爱护路由的办法叫做路由守卫,次要用来通过跳转或勾销的形式守卫导航。路由守卫有三个级别:全局、路由独享、组件级。影响范畴由大到小,例如全局的router.beforeEach(),能够注册一个全局前置守卫,每次路由导航都会通过这个守卫,因而在其外部能够退出管制逻辑决定用户是否能够导航到指标路由;在路由注册的时候能够退出单路由独享的守卫,例如beforeEnter,守卫只在进入路由时触发,因而只会影响这个路由,管制更准确;咱们还能够为路由组件增加守卫配置,例如beforeRouteEnter,会在渲染该组件的对应路由被验证前调用,管制的范畴更准确了。用户的任何导航行为都会走navigate办法,外部有个guards队列按程序执行用户注册的守卫钩子函数,如果没有通过验证逻辑则会勾销原有的导航。原理 runGuardQueue(guards)链式的执行用户在各级别注册的守卫钩子函数,通过则持续下一个级别的守卫,不通过进入catch流程勾销本来导航 ...

January 2, 2023 · 6 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属性。 ...

January 2, 2023 · 3 min · jiezi

关于vue.js:Vue学习笔记五-长乐无极

下一篇是长乐未央,其实是我记错了,将长乐未央、长毋相忘,记成了长乐无极,长乐未央。在汉代,长乐未央和长毋相忘,是两句罕用的祝福语,意思是短暂高兴,永远不要遗记对方。前面的主线就是通过一些例子来介绍Vue的一些罕用概念。以后这个例子仍然来自mdn web docs。加上了我本人的了解。进化: 组件列表书接上回(有种章回体的感觉了,哈哈),上回咱们构建了一个待办组件,并借助props来实现父子组件之间进行通信。父子组件简略的说就是假如一个组件,咱们权且称之为A,应用了另一个组件,咱们权且称之为B。那么咱们称A为父组件,B为子组件,也就是父子组件。然而咱们的待办通常不会只有一个,假如咱们有10个待办,咱们不会想将组件标签写10次,咱们会想到循环,没错这就是咱们前文中提到的v-for指令,首先咱们在App.vue中申明一个data函数,而后在函数中咱们返回的对象中蕴含一个数组。 data() { return { ToDoItems: [ { label: 'JavaFX教程(一)', done: false }, { label: '应用JavaFX打包一个平台包', done: true }, { label: '写C语言教程', done: true }, { label: 'SDL库', done: false } ]};目前如同没问题,然而咱们并不想每次批改待办,都会将所有的待办都从新创立一遍,为了帮忙Vue优化列表中元素的出现,Vue须要在v-for创立进去的元素有惟一的键,这些键最好是字符串或数值,这里咱们就能够抉择应用第三方库来产生惟一的键, 也就是lodash包的uniqueid()办法来产生惟一键。首先让咱们进行服务,而后再终端中输出以下命令: npm install --save lodash.uniqueid# 如果报 找不到uniqueidnpm i --save-dev @types/lodash.uniqueid而后在app.vue中引入这个这个办法, import uniqueId from 'lodash.uniqueid';而后咱们的app.vue就变成了上面这样: <template> <div id="app"> <h1>To-Do List</h1> <ul> <li v-for="item in ToDoItems" :key="item.id"> <to-do-item :label="item.label" :done="item.done" ></to-do-item> </li> </ul> </div></template><script>import ToDoItem from './components/ToDoItem.vue'import uniqueId from 'lodash.uniqueid'export default { name: 'App', components: { ToDoItem }, data () { return { ToDoItems: [ { id: uniqueId('todo-'), label: 'JavaFX教程(一)', done: false }, { id: uniqueId('todo-'), label: '应用JavaFX打包一个平台包', done: true }, { id: uniqueId('todo-'), label: '写C语言教程', done: true }, { id: uniqueId('todo-'), label: 'SDL库', done: false } ] } }}</script><style>#app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px;}</style>目前app的css不会发生变化,前面的代码示例就临时不展现。咱们当初曾经为每一个待办产生了一个id,那待办组件的id就显的有点多余了,咱们将待办组件的id申明为prop,同时移除掉lodash相干的代码, 当初待办组件的script就变成了上面这样: ...

January 1, 2023 · 6 min · jiezi

关于vue.js:Vue学习笔记四-久处不厌

原本打算写JavaFX的,然而比拟令人悲伤的是,那一篇博客没有同步到GitHub上,还在公司的电脑上。这篇还在。前言学习本篇须要有肯定的Vue、Node、HTML、JavaScript、CSS根底,须要一些预置概念。 《Vue学习笔记(一) 初遇篇》介绍Vue的源起以及Vue的根本示例, 《Vue学习笔记(二) 相识篇》对《Vue学习笔记(一) 初遇篇》进行重构,从新组织了《Vue学习笔记(一)初遇篇》的内容: 随着网页越来越简单,开发者们在谋求越来越快的开发速度和性能,这也就是古代框架的缘起,绝对于JQuery这样间接操纵DOM结点的库,导致页面重绘影响页面的性能。古代前端框架推出了虚构DOM不再让开发者间接操纵DOM结点,同时引入了响应式、模板语法进一步放慢开发前端页面的速度,然而仅仅如此还不够,古代前端框架开始谋求可复用性,这也就是组件,JavaScript、CSS、HTML的复合体,让咱们能够复用逻辑、款式、性能。 当初咱们来回顾一下《Vue学习笔记(三) 》,这篇文章外面咱们讲了事件零碎、监听事件、模块化、模块化的益处。以及JavaScript的模块化倒退历史,JavaScript的如何应用模块, 表单的输出与绑定、组件的简略应用。但对于Vue的外围概念组件来说咱们并未介绍多少,咱们只是让她小小的露了个面,演示了一个组件如何申明,如何应用。但这种应用组件的形式并不讨喜,起因在于这种形式的组件的内容: HTML、CSS、JavaScript都在字符串外面。比拟适应的是逻辑、款式都不简单的场景,一旦这些内容略微宏大一点点,宏大的字符串就会让咱们头晕脑胀。 对此的解决方案是单文件组件,将文件放在一个文件中,后缀为.vue。但咱们想接着开发前端利用还不大够,你会发现前面单文件利用基本上都会抉择用Vue CLI来构建, 那Vue CLI 是什么? Vue咱们是意识的,那CLI呢?首先CLI是Commend-Line Interface的缩写, Commend-Line Interface 命令行界面,用户通过键盘输入指令,计算机接管指令后,予以执行。下面是维基百科的定义,上面咱们来看下Vue CLI是如何介绍本身的: Vue CLI 是一个基于 Vue.js 进行疾速开发的残缺零碎,提供: 通过 @vue/cli 实现的交互式的我的项目脚手架。通过 @vue/cli + @vue/cli-service-global 实现的零配置原型开发。一个运行时依赖 (@vue/cli-service),该依赖: 可降级;基于 webpack 构建,并带有正当的默认配置;能够通过我的项目内的配置文件进行配置;能够通过插件进行扩大。一个丰盛的官网插件汇合,集成了前端生态中最好的工具。一套齐全图形化的创立和治理 Vue.js 我的项目的用户界面。Vue CLI 致力于将 Vue 生态中的工具根底标准化。它确保了各种构建工具可能基于智能的默认配置即可安稳连接,这样你能够专一在撰写利用上,而不用花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。 咱们再来回顾一下《让咱们来来前端工程化》探讨了什么: 随着浏览器页面的越来越简单,许多问题变浮现了进去,兴许许多事件都是这样,当问题的规模比拟小的时候,通常比拟容易解决。一旦问题的规模开始增长,问题的解决难度也随着上涨。页面越来越简单,那就意味着代码量也在回升,晚期的页面简略,JavaScript的设计者没有为这门语言筹备模块化,尽管语言自身没有自带,JavaScript社区就自发的为JavaScript设计模块,比拟为人所熟知的模块化零碎是: AMD —— 最古老的模块零碎之一,最后由require.js库来实现。CommonJS —— 为Node.js服务器创立的模块零碎。UMD —— 另外一个模块零碎,倡议作为通用的模块零碎,它与AMD和CommonJS都兼容。当初 ,它们都在缓缓成为历史的一部分,但你依然能够在旧有的我的项目找到它们,语言级别的模块零碎在2015年的时候呈现在了规范(ES6)中,尔后逐步倒退,当初曾经失去了所有支流浏览器和Node.js的反对。 模块化解决了在JavaScript如何复用代码的问题,从肯定水平上来说升高了代码量和代码复杂度。但这仅仅解决了一个问题,另一个值得注意的问题是,在应用了npm的状况下,最终的页面该如何交付,开发页面的时候咱们还有CSS、SASS、JPG等各种资源,咱们该如何组织这些文件,WebPack是这个问题的一个答案,WebPack会剖析JavaScript之间的依赖关系,将咱们我的项目的文件进行分类,放到对应的文件夹里。WebPack这是一个打包工具,它提供的能力还有模块捆绑,所谓模块捆绑指的是将页面所需的模块捆绑后能力一个大文件或几个文件以缩小申请数量。 除此之外,还须要面对的问题是适配低版本浏览器的问题(IE浏览器走开),有些JavaScript语法好用然而低版本的不反对,但能不能咱们只写一套,由工具帮咱们主动产生低版本浏览器的代码呢? 这也就是Babel。 为了疾速高质量的构建我的项目,咱们引入了一个又一个工具,然而该如何疾速的搭建起一个我的项目的模板呢? 咱们的愿景是这些的通过一些命令行命令就能将webpack、node.js、babel都引入到我的项目中,并且这些外面曾经写了一些默认配置,有一个简略的示例。我只须要写我须要的页面就行了。这也就是Vue CLI。 再读一遍Vue自我介绍的话: Vue CLI 致力于将 Vue 生态中的工具根底标准化。它确保了各种构建工具可能基于智能的默认配置即可安稳连接,这样你能够专一在撰写利用上,而不用花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。开启第一个单文件我的项目吧本我的项目首先须要一个node环境,要求Node的版本在14.18.1以上,尽管Vue 的最新大版本曾经到了3.2了,但咱们这里的Vue大版本抉择还是2.5.2。前面会专门再钻研3和2的区别。咱们本次借助Vue CLI来构建我的项目,咱们借助VS Code来编写代码,我的VS Code装置的插件有: ...

January 1, 2023 · 3 min · jiezi

关于vue.js:vue3个人心得功能详解六模板引用ref

ref的作用原始对 DOM 的操作,应用的是通过ID与getElementById办法来操作,如下: <script setup>function uu(){console.log( document.getElementById('itemR'))}//借助getElementById办法及ID操作DOM</script><template> <button v-on:click="uu"></button> <li id="itemR"> 我来试试 </li> </template>ref不须要用到 getElementById 间接应用ref名即可对 DOM 进行操作,如下: import {ref} from 'vue'const itemR=ref()function uu(){console.log(itemR.value)}//间接应用ref名操作DOM</script><template> <button v-on:click="uu"></button> <li ref="itemR"> 我来试试 </li></template>ref的写法模板部通过标签的ref属性给DOM赋标识符 <li ref="标识符">选项式API通过$fefs对象来操作ref标识的DOM this.$refs.标识符组合式API不再应用$refs对象,而是间接应用标识符来操作,不过须要先用ref声名这个标识符。 const 标识符=ref() 标识符.value留神此处的ref申明的响应变量不是一般的响应变量,而是DOM响应变量,他的值是DOM对象。 ref生命周期问题ref属性是在DOM 渲染阶段生成,因而在渲染生成前应用ref会失去空的DOM,只有在渲染实现后能力应用ref操作DOM,如下ref将操作不了DOM: <script setup>import {ref,reactive,onMounted,onBeforeMount} from 'vue'const itemR=ref()console.log(itemR.value)//打印不出<li>标签</script><template> <li ref="itemR"> 我来试试 </li> </template>放在onMounted里,能力胜利操作DOM,如下: <script setup>import {ref,reactive,onMounted,onBeforeMount} from 'vue'const itemR=ref()onMounted(()=>{console.log(itemR.value)})//胜利打印出<li>标签</script><template> <button v-on:click="uu"></button> <li ref="itemR"> 我来试试 </li></template>ref的标识名字符型标识数组型标识变量型标识

December 31, 2022 · 1 min · jiezi

关于vue.js:vue-scope和deep穿透原理

如果vue组件有带scoped的style,postcss编译时 会为该组件内所有dom元素都加一个【标识组件惟一】的动静属性[哈希值,data-v-实例标识](这里dom,包含 [一般dom元素] 和 [子组件的根元素] )为style<scoped>标签下所有,不蕴含[deep深度选择器]的选择器最初一级增加属性选择器[哈希值,data-v-实例标识]<div class="a"> <div class="b">登录</div></div><style lang="less" scoped>.a { .b { background-color:#bfa }}</style>编译后 <div data-v-257dda99b class="a"> <div data-v-257dda99b class="b">登录</div></div><style scoped>.a .b[data-v-257dda99b] { background-color:#bfa}</style>deep深度选择器的原理很简略,vue不会给深度选择器前面的选择器单元减少 属性选择器[哈希值,data-v-实例标识] 深度选择器css对应 >>>less/scss等预处理对应 /deep/ , ::v-deep 和最新的 :v-deep() // 父组件<template> <div class="parent" id="app"> <h1>我是父组件</h1> <child></child> </div></template><style lang="less" scoped> // 把子组件的背景变成红色;然而并不起作用 .child .dyx { background-color: red; } // 加上deep;起作用 ::v-deep .child .dyx { background-color: red; }</style>// 子组件<template> <div class="child"> <h1>我是子组件</h1> <div class="dyx"> <p>我是子组件的段落</p> </div> </div></template>编译后 <div data-v-257dda99b class="parent" id="app"> <h1 data-v-257dda99b>我是父组件</h1> <div data-v-xxxxxxx class="child"> <h1 data-v-xxxxxxx>我是子组件</h1> <div data-v-xxxxxxx class="dyx"> <p data-v-xxxxxxx>我是子组件的段落</p> </div> </div></div>[data-v-257dda99b] .child .dyx { background-color: red;}.dyx前面的属性选择器没了,所以款式能够失常穿透到子组件失效。 ...

December 29, 2022 · 1 min · jiezi

关于vue.js:Vue自定义指令实现加载中效果vload不使用Vueextend

网站成果演示:http://ashuai.work:8888/#/myLoadGitHub仓库地址代码:https://github.com/shuirongsh...加载中思路剖析实现加载中成果,个别有两种形式: 第一种是:搞一个load组件,而后应用Vue.extend()办法去继承一个加载组件去应用,比方笔者的这篇文章:https://segmentfault.com/a/11...第二种是:间接应用指令去在须要加载的dom下来创立一个加载中的dom元素,并指定相应的款式即可。本篇文章说的是第二种。咱们先看一下效果图 v-load效果图 实现步骤一:加上自定义指令假如我有一个dom元素,我给其加上一个自定义的指令v-load="loading",绑定一个具体的布尔值loading,用于管制开启加载中和敞开加载中 <div class="box" v-load="loading">111</div>loading: true.box { width: 160px; height: 80px; border: 2px solid #666;}接下来,我就要在自定义指令的相干钩子函数中去操作这个dom元素。 对于自定义指令的入门基础知识能够看官网文档,或者参见笔者之前的对于自定义指令的文章:https://segmentfault.com/a/11...实现步骤二:给指标元素创立一个子元素dom用于加载在自定义指令的初始化bind钩子函数中,咱们能够拿到这个dom元素,首先给这个指标元素开始绝对定位,让用于加载中的子元素dom去相对定位,以这个绝对定位的父元素进行参考 bind(el, binding) { const target = el; // 父元素绝对定位 target.style.position = "relative"; // 子元素遮罩层局部 let loadChild = document.createElement("div"); loadChild.className = "loadClass";}上述代码中给加载中的子元素loadChild指定一个款式类名loadClass 在这里小伙伴可能有疑难了,这个自定义指令的款式怎么写呢?自定义指令中也没有style标签啊? 是的,自定义指令中不能间接写款式,不过没关系,咱们能够先写好一个css款式,而后引入过去应用啊,如下: // 引入拆分的款式,便于自定义指令中应用import './index.css'bind(el, binding) { ...... loadChild.className = "loadClass";}这样的话,className的款式,能够在引入的同级目录下的./index.css文件中设置了,loadClass款式如下: .loadClass { /* 宽高百分百 */ position: absolute; top: 0; bottom: 0; left: 0; right: 0; /* 默认背景色和色彩 */ background-color: rgba(255, 255, 255, 0.99); color: #0b6ed0; /* 透明度过渡应用搭配display:none; */ opacity: 0.8; transition: all 0.3s; /* 居中 */ display: flex; align-items: center; justify-content: center;}留神,加载中成果开启和隐没,不必应用vue自带的过渡组件transition,咱们能够应用透明度搭配搭配display:none;去设置 ...

December 29, 2022 · 3 min · jiezi

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

本篇文章记录仿写一个el-button组件细节,从而有助于大家更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在github上,大家能够拉下来,npm start运行跑起来,联合正文有助于更好的了解 网站成果演示:http://ashuai.work:8888/#/myB... GitHub仓库地址:https://github.com/shuirongsh... 什么是Button组件按钮用于点击,个别是做事件的响应。 按钮封装效果图 按钮分类繁多按钮 默认按钮主题按钮(primary、success、warning、error)按钮大小(small、middle、big)按钮禁用(disabled)按钮加载(loading)按钮的图标地位(默认图标在按钮文字左侧)图标按钮(没有按钮文字)繁多文字按钮按钮组(按钮组中有多个按钮)默认按钮默认按钮很简略,只是写一个最一般的款式即可 <button :class="[ 'myButton' ]" /> .myButton { display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; box-sizing: border-box; padding: 12px 16px; background-color: rgba(0, 0, 0, 0.1); color: #222; border: none; cursor: pointer; user-select: none; // 不让选中文字 transition: all 0.3s; font-size: 14px;}// 悬浮成果.myButton:hover { background-color: rgba(0, 0, 0, 0.2);}// 按中成果.myButton:active { background-color: rgba(0, 0, 0, 0.3);}笔者这里是将悬浮的成果和按中的成果,设置背景色越来越深。这样的话,看着成果比拟显著 主题按钮所谓按钮的主题,就是增加不同的类名,比方primary主题的按钮,就加上.primary类名、success主题的按钮,就加上.success类名。而后应用动静class去增加即可(这里应用动静class的数组用法)。如: <button :class="[ 'myButton', type ]" /> ...

December 28, 2022 · 5 min · jiezi