关于vue3:VUE在data中监听新的data

Data:  {          key: 5,          prop: "colorBook",          label: "高模模型文件",          render: (scope) => {            const { $index } = scope;            const thisProp = `3dModel_sil3dHighModel_${$index}`;            let ele = [];            if (!this.$data[thisProp]) {              this.$data[thisProp] = [];            }            console.log(this);            ele = (              <ElFormItem                prop={thisProp}                label-width={0}                style={{ marginBottom: 0 }}              >                <FilesUpload                  prop={thisProp}                  limit={3}                  uploadFunction={this.uploadFunction}                  removeFunction={this.removeFunction}                  fileList={this.$data[thisProp]}                />              </ElFormItem>            );            return ele;          },        },Methods:     async uploadFunction(file, prop) {      let type = "";      switch (true) {        case /sil3dHighModel/.test(prop):          type = "3dHighModel";          break;        default:          break;      }      const formData = new FormData();      formData.append("file", file);      const res = await uploadPatternImage(formData, { type });      let result = false;      if (res.code === 200) {        if (!this.$data[prop]) {          this.$data[prop] = [];        }        this.$data[prop].push({ name: res.data, url: res.data });        console.log(this);        this.patternInfo[prop] = this.$data[prop];        result = true;      }      return result;    },如上的代码,第一段是监听在data中的一段渲染表格列的代码,而后因为表格有个用户点击自增行的性能,所以fileList的key可能有有数多个,名字也不能确定,须要通过逻辑去减少。后面尝试了一种写法this[prop],发现尽管this.[prop]是有变动的,但并不能触发页面的update。初步断定this外面属于data的字段是VUE深拷贝的正本,由data的变动在proxy的set外面驱动。 解决方案:通过间接设置在this.$data外面,视乎绕过了VUEdata()的拷贝逻辑,尽管没有在this中监听,然而能从this.$data中取到,并且失常触发页面update更新。

July 29, 2021 · 1 min · jiezi

关于vue3:vite引入elementPlus打包报错Cannot-find-name-Nullable

vite在build的时候elementPlus哗啦啦的报了一堆的错 而后我。。。就在官网的issues上翻到了同样的问题就看到有大佬提供了解决方案 完满解决

July 7, 2021 · 1 min · jiezi

关于vue3:Vue3问题总结

Uncaught DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rulesvue3+vue-router4在应用过程中,通过composition api watch对router对象进行监听时,会呈现这个问题。 import { useRoute } from 'vue-router'const route = useRoute()watch( route, (to, from) => { // do something } )解决办法,别监听整个router对象,改为监听其某个属性,如监听path的变动就能够了。 Uncaught (in promise) DOMException: Failed to read the 'value' property from 'SVGLength': Could not resolve relative length.我的项目中用到了svg,同时做了svg依据主题色进行换色解决的性能(监听了路由的变动去解决),打包部署后呈现这个问题,解决办法如第一个。

July 3, 2021 · 1 min · jiezi

关于vue3:记录几个vue3-demo项目开发的问题

结尾最近在学习vue3(应用开发层面),做了一些小demo我的项目,像[md-editor-v3],vue3-admin等。喜爱在vue我的项目中应用jsx语法来编写,简直我的项目中都应用到了ts,某一些写法,在谷歌的时候没找到,所以想做一个小总结。 之前看他人探讨过在我的项目中使ts的态度,作者持保留态度,有些中央会减少代码量,如果不相熟的话,甚至会耽搁开发进度。然而从开发公共组件及某些复用组件来看,ts会帮助开发,而且可能减少浏览性。如果文章内容有谬误,请帮助指出。 ts中的props该问题次要存在于应用了ts开发的我的项目中,不区别是否应用jsx,从 string、string/object混合、array 角度演示(default值没有写出)。 js开发组件中,props类型验证会像上面这样写 props: { propA: String, propB: [String, Object], propC: [Array] }如果A只能是a\b\c三个中的一个,B如果只能是css款式,对象就须要限度,C只能是数据中存在a/b/c中的组合,你可能会用到validator。然而没有类型,对开发提醒不敌对。 于是应用ts替换一下写法, import { PropType, CSSProperties } from 'vue';props: { propA: String as PropType<'a' | 'b' | 'c'>, propB: [String, Object] as PropType<string | CSSProperties>, propC: [Array] as PropType<Array<'a' | 'b' | 'c'>>,}能领会到的不言而喻的益处就是上面这样: 懒人必备的代码提醒~ 看过ant-design-vue的源码的话,会发现他们应用了一个叫vue-types的库来封装了类型,适合的能够间接应用这种形式,传送门 不必去给setup的第一入参定义类型,会和props抵触,感兴趣的能够试试。 jsx中的插槽这个在vue3-admin中有用card组件做过演示,传送门 开发组件须要留神的是,如果让组件更好的反对jsx的话,就须要思考组件的插槽内容是通过props传递的还是通过插槽的形式传递的,前者多呈现在jsx语法中,而后者则少数状况是.vue模板语法中。 剖析card组件import { SetupContext, EmitsOptions, } from 'vue';setup(props, ctx: SetupContext<EmitsOptions>) { return () => { // 插槽获取肯定在函数组件(render)办法外部,否则提醒:this will not track dependencies used in the slot const slotDefault = getSlot({ ctx }); const slotTitle = getSlot({ props, ctx }, 'title'); const slotFunc = getSlot({ props, ctx }, 'func'); const cardClass = classnames('vra-card', props.border && 'vra-card-border'); const headerClass = classnames('vra-card-header', props.headerClass); const bodyClass = classnames('vra-card-body', props.bodyClass); return ( <div class={cardClass}> {slotTitle && ( <div class={headerClass} style={props.headerStyle}> <div class="vra-card-title">{slotTitle}</div> {slotFunc && <div class="vra-card-func">{slotFunc}</div>} </div> )} <div class={bodyClass} style={props.bodyStyle}> {slotDefault} </div> </div> ); }; }getSlot办法是用来依据名称来主动获取起源中的插槽内容的,不论是vue插槽的形式,还是jsx的属性的形式,代码: ...

June 30, 2021 · 2 min · jiezi

关于vue3:vue30的入门使用

1.createApp // 顾名思义,创立一个APP实例 // 先导入createApp模块import { createApp } from 'vue';import App from './App.vue';// 应用createApp办法将咱们的入口文件放进去,最初挂载createApp(App).mount('#app');2.onMounted // 跟之前的写法不一样了不过,多了一个on // 先导入onMounted模块import { onMounted, defineComponent } from 'vue';export default defineComponent({ setup () { // 应用的时候须要放在setup里边 onMounted(() => { console.log('组件挂在完结开始打印。。。') }) }})3.computed 计算机属性 import { computed, ref } from 'vue';// 基本操作const count = ref(1)const plusOne = computed(() => count.value + 1)console.log(plusOne.value) // 2plusOne.value++ // 谬误!import { computed, ref } from 'vue';// 能够批改值const count = ref(1)const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 },})plusOne.value = 1console.log(count.value) // 04.watch 监听器 ...

June 25, 2021 · 2 min · jiezi

关于vue3:Vue3ElementPlusKoa2-全栈开发后台系统

download:Vue3+ElementPlus+Koa2 全栈开发后盾零碎<!-- <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>JS贪吃蛇</title> <style type="text/css"> #pannel table { border-collapse: collapse; } #pannel table td { border: 1px solid #808080; width: 10px; height: 10px; font-size: 0; line-height: 0; overflow: hidden; } #pannel table .snake { background-color: green; } #pannel table .food { background-color: blue; }</style><script type="text/javascript">var Direction = new function () { this.UP = 38; this.RIGHT = 39; this.DOWN = 40; this.LEFT = 37;};var Common = new function () { this.width = 20; /*程度方向方格数*/ this.height = 20; /*垂直方向方格数*/ this.speed = 250; /*速度 值越小越快*/ this.workThread = null;};var Main = new function () { var control = new Control(); window.onload = function () { control.Init("pannel"); /*开始按钮*/ document.getElementById("btnStart").onclick = function () { control.Start(); this.disabled = true; document.getElementById("selSpeed").disabled = true; document.getElementById("selSize").disabled = true; }; /*调速度按钮*/ document.getElementById("selSpeed").onchange = function () { Common.speed = this.value; } /*调大小按钮*/ document.getElementById("selSize").onchange = function () { Common.width = this.value; Common.height = this.value; control.Init("pannel"); } };};/*控制器*/function Control() { this.snake = new Snake(); this.food = new Food(); /*初始化函数,创建表格*/ this.Init = function (pid) { var html = []; html.push("<table>"); for (var y = 0; y < Common.height; y++) { html.push("<tr>"); for (var x = 0; x < Common.width; x++) { html.push('<td id="box_' + x + "_" + y + '"> </td>'); } html.push("</tr>"); } html.push("</table>"); this.pannel = document.getElementById(pid); this.pannel.innerHTML = html.join(""); }; /*开始游戏 - 监听键盘、创立食物、刷新界面线程*/ this.Start = function () { var me = this; this.MoveSnake = function (ev) { var evt = window.event || ev; me.snake.SetDir(evt.keyCode); }; try { document.attachEvent("onkeydown", this.MoveSnake); } catch (e) { document.addEventListener("keydown", this.MoveSnake, false); } this.food.Create(); Common.workThread = setInterval(function () { me.snake.Eat(me.food); me.snake.Move(); }, Common.speed); };}/*蛇*/function Snake() { this.isDone = false; this.dir = Direction.RIGHT; this.pos = new Array(new Position()); /*挪动 - 擦除尾部,向前挪动,判断游戏完结(咬到本人或者移出边界)*/ this.Move = function () { document.getElementById("box_" + this.pos[0].X + "_" + this.pos[0].Y).className = ""; //所有 向前挪动一步 for (var i = 0; i < this.pos.length - 1; i++) { this.pos[i].X = this.pos[i + 1].X; this.pos[i].Y = this.pos[i + 1].Y; } //从新设置头的地位 var head = this.pos[this.pos.length - 1]; switch (this.dir) { case Direction.UP: head.Y--; break; case Direction.RIGHT: head.X++; break; case Direction.DOWN: head.Y++; break; case Direction.LEFT: head.X--; break; } this.pos[this.pos.length - 1] = head; //遍历画蛇,同时判断游戏完结 for (var i = 0; i < this.pos.length; i++) { var isExits = false; for (var j = i + 1; j < this.pos.length; j++) if (this.pos[j].X == this.pos[i].X && this.pos[j].Y == this.pos[i].Y) { isExits = true; break; } if (isExits) { this.Over(); /*咬本人*/ break; } var obj = document.getElementById("box_" + this.pos[i].X + "_" + this.pos[i].Y); if (obj) obj.className = "snake"; else { this.Over(); /*移出边界*/ break; } } this.isDone = true; }; /*游戏完结*/ this.Over = function () { clearInterval(Common.workThread); alert("游戏完结!"); location.reload(); }/*吃食物*/ this.Eat = function (food) { var head = this.pos[this.pos.length - 1]; var isEat = false; switch (this.dir) { case Direction.UP: if (head.X == food.pos.X && head.Y == food.pos.Y + 1) isEat = true; break; case Direction.RIGHT: if (head.Y == food.pos.Y && head.X == food.pos.X - 1) isEat = true; break; case Direction.DOWN: if (head.X == food.pos.X && head.Y == food.pos.Y - 1) isEat = true; break; case Direction.LEFT: if (head.Y == food.pos.Y && head.X == food.pos.X + 1) isEat = true; break; } if (isEat) { this.pos[this.pos.length] = new Position(food.pos.X, food.pos.Y); food.Create(this.pos); } }; /*管制挪动方向*/ this.SetDir = function (dir) { switch (dir) { case Direction.UP: if (this.isDone && this.dir != Direction.DOWN) { this.dir = dir; this.isDone = false; } break; case Direction.RIGHT: if (this.isDone && this.dir != Direction.LEFT) { this.dir = dir; this.isDone = false; } break; case Direction.DOWN: if (this.isDone && this.dir != Direction.UP) { this.dir = dir; this.isDone = false; } break; case Direction.LEFT: if (this.isDone && this.dir != Direction.RIGHT) { this.dir = dir; this.isDone = false; } break; } };}/*食物*/function Food() { this.pos = new Position(); /*创立食物 - 随机地位创建设*/ this.Create = function (pos) { document.getElementById("box_" + this.pos.X + "_" + this.pos.Y).className = ""; var x = 0, y = 0, isCover = false; /*排除蛇的地位*/ do { x = parseInt(Math.random() * (Common.width - 1)); y = parseInt(Math.random() * (Common.height - 1)); isCover = false; if (pos instanceof Array) { for (var i = 0; i < pos.length; i++) { if (x == pos[i].X && y == pos[i].Y) { isCover = true; break; } } } } while (isCover); this.pos = new Position(x, y); document.getElementById("box_" + x + "_" + y).className = "food"; };}function Position(x, y) { this.X = 0; this.Y = 0; if (arguments.length >= 1) this.X = x; if (arguments.length >= 2) this.Y = y;}</script></head> ...

June 24, 2021 · 4 min · jiezi

关于vue3:vue3配置jest测试环境踩坑

根本配置网上都有,这里不再详述,说一下踩过的坑 目前jest只能用26+的版本,不能用最新的27+,我一开始间接用的npm install jest --save-dev装置,各种配置配好后运行报了一个莫名其妙的谬误Cannot destructure property 'config' of 'undefined',查看源码发现是vue-jest中getCacheKey的第4个参数解构失败报错,再搜寻了一下发现是jest调用vue-jest的getCacheKey办法的时候只传了3个参数,由此判断是jest版本有问题,再看了一下element-plus的配置发现用的是jest26版本,豁然开朗,马上换26版本运行,发现还是报错不过是另一个谬误了。钻研了下,应该是jest配套的包的版本问题,于是全副换成26+运行,胜利。上面是我胜利运行的的package.json中jest相干的包配置: "@types/jest": "^26.0.23", "babel-jest": "^26.3.0", "jest": "^26.6.3", "ts-jest": "^26.0.0",运行胜利后,我本人写的一个dialog组件测试用例执行失败,谬误:Cannot call text on an empty DOMWrapper,具体代码: const TESTSTR = 'risk everywhere risk everywhere risk everywhere';describe('Dialog vue',() => { test('dialog should have content when content has been given', async () => { const wrapper = mount(Dialog,{ props:{ content: TESTSTR, modelValue: true } }); await nextTick(); expect(wrapper.find('.modal-body').text()).toEqual(TESTSTR); });});其它组件测试没报错,就这个dialog组件报错,通过了一番思考终于找到起因: <template> <teleport to="body"> </teleport></template>teleport的问题,元素都搬走了,DOMWrapper必定变成empty了,应该要加一个配置属性能让teleport生效 <template> <teleport to="body" :disabled="!appendToBody"> </teleport></template>OK,功败垂成 ...

June 19, 2021 · 1 min · jiezi

关于vue3:Vue3-优雅的模态框封装方案-初探

my-blog:https://tuimao233.gitee.io/ma... Vue3 优雅的模态框封装办法 - 初探 Vue3 优雅的模态框封装办法 - 实际 想必大家应用 Vue 开发时,都有应用过 Element 或 Ant Design of Vue 的模态框 例如 Ant Design of Vue 中的 a-message: <a-modal v-model="visible" title="Title" on-ok="handleOk"> 自定义内容 </a-modal>又或者间接在 js 代码中间接调起的 ElMessageBox: import { ElMessageBox } from 'element-plus';ElMessageBox.confirm('此操作将永恒删除该文件, 是否持续?', '提醒') .then(()=> { }) .catch(()=> { })例如在我的项目当中,有须要定义模态框调用的场景,就须要把握自定义模态框的封装 就好比方在一个后盾管理系统中的一个图片选择器 ImageSelect({ multiple: true }) .then((images)=> { })又或者是应用组件库的模态框自定义款式性能,导致须要写全局类名笼罩组件模态框,复用率低下 <!-- 组件 A --> <el-dialog custom-class="purchase-dialog"> ...自定义内容... </el-dialog> <!-- 组件 B --> <el-dialog custom-class="purchase-dialog"> ...自定义内容... </el-dialog>可见要封装好一个模态框组件的重要性还是很高的,那么整个纲要中会循环渐进的通知你如果须要自定义封装一个又反对在 template 中应用,又反对在 js 代码中间接调用的对话框,以及如何利用组件库已有的模态框在不影响其复用性,扩展性的前提下进行二次封装。 ...

June 18, 2021 · 1 min · jiezi

关于vue3:和面试官聊聊DiffVue3

这是 聊diff 的第三篇文章,聊聊vue3的diff思路.思路次要来自 vue-design 我的项目【第一篇】和面试官聊聊Diff___React【第二篇】和面试官聊聊Diff___vue2【第三篇】和面试官聊聊Diff___Vue3(本文) 为了更好的浏览体验,倡议从第一篇看起 我是一名前端的小学生。行文中对某些设计原理了解有误非常欢送大家探讨斧正,谢谢啦!当然有好的倡议也谢谢提出来(玩笑) Let's start Vue3_diff过程剖析本文重视的是patch过程,具体的细节和边界就没有思考。 ==另 外 注 意== 三篇文章 diff 的解说,为了不便展现 节点复用, 用了 children 保留内容,实际上这是不合理的,因为children不同还会递归补丁(patch)diff也不是vue optimize的全副,只是其中一部分,例如compile时确定节点类型,不同类型 不同的 mount/patch 解决形式等等。Vue2.x的 diff 绝对于 react 更好一些,防止了一些不必要的比对。我先假如有如下节点, key 是 Vnode 的 key, children 代表该节点的内容 // 以前的节点const preNodes = [ {key: "k-1", children: "<span>old1</span>"}, {key: "k-2", children: "<span>old2</span>"}, {key: "k-3", children: "<span>old3</span>"}, {key: "k-4", children: "<span>old4</span>"}, {key: "k-5", children: "<span>old5</span>"}, {key: "k-6", children: "<span>old6</span>"},]// 新节点,最初更新的后果const nextNodes = [ {key: "k-11", children: "<span>11</span>"}, {key: "k-0", children: "<span>0</span>"}, {key: "k-5", children: "<span>5</span>"}, {key: "k-13", children: "<span>13</span>"}, {key: "k-1", children: "<span>1</span>"}, {key: "k-7", children: "<span>7</span>"}, {key: "k-16", children: "<span>16</span>"}, {key: "k-3", children: "<span>3</span>"}, {key: "k-15", children: "<span>15</span>"}, {key: "k-17", children: "<span>7</span>"}, {key: "k-4", children: "<span>4</span>"}, {key: "k-6", children: "<span>6</span>"}]diff 是基于新旧的 diff, 先要明确这个大前提,如果刚刚开始没有节点,则会先 mount 而不会 patch。最初冀望的后果(老节点都失去了复用) ...

June 15, 2021 · 5 min · jiezi

关于vue3:从零开始的electron开发文件处理本地文件加载

文件解决-本地文件加载咱们在应用electron时,有时会波及一些文件的解决,比方文件的下载,或者本地文件的加载(本地音乐,本地图片等),本章次要介绍electron本地文件的加载。 其实这个性能还是比拟常见的,比方咱们下载了某某皮肤主题本地,想在本地加载,或者是做一个音乐播放器,加载本地音乐进行播放。 指标:应用input file获取图片或者音乐文件的本地地址(当然你能够间接用已有文件的本地地址),进行展现和播放。 web本地文件加载浏览器为了平安思考,在web页面进行文件加载时,是禁用了file://协定进行文件展现的,一般来说要想获取本地文件展现,得让用户进行input file抉择,获取File对象,对这个File对象进行操作展现: <a-upload :customRequest="customRequest" name="file" :showUploadList="false" :multiple="true"> <a-button> <upload-outlined></upload-outlined> 增加图片 </a-button></a-upload><a-image :width="200" :height="200" :src="state.image" />function customRequest(fileData) { const file = fileData.file state.image = window.URL.createObjectURL(file) // 或者: if (file) { var reader = new FileReader() reader.onload = function (evt) { state.image = evt.target.result } reader.onerror = function (evt) { console.error(evt) } reader.readAsDataURL(file) }}比方在进行图片的本地显示时,应用createObjectURL间接创立url对象进行展现或者应用readAsDataURL将其转化为base64进行展现。 当然在electron中所有都变得简略起来,咱们能够应用本地门路加载文件,当然得进行一些小解决。 electron本地文件加载比如说咱们已知一个本地图片的门路,假如这个门路为下载文件夹中C:\Users\Administrator\Downloads\1.png,咱们将这个地址赋值给img的src: <a-upload :customRequest="customRequest" name="file" :showUploadList="false" :multiple="true"> <a-button> <upload-outlined></upload-outlined> 增加图片 </a-button></a-upload><a-image :width="200" :height="200" :src="state.image" />function customRequest(fileData) { const path = fileData.file.path state.image = path}这里的path就是本地文件的地址,当你赋值之后发现图片并不能加载,会报一个net::ERR_UNKNOWN_URL_SCHEME的谬误,这是因为间接增加本地门路的话,加载文件实际上是通过file://协定进行加载的,默认状况下chromium并不能通过file://协定来读取文件,参考链接, 故并不能间接显示进去,原本能够设置chromium启动参数(–-allow-file-access-from-files)来解决这个问题,然而比拟遗憾electron并不吃这个一套: ...

June 15, 2021 · 1 min · jiezi

关于vue3:vue30父组件调用子组件中的方法

父组件ParentComponents.vue <!-- 1. 在子组件上绑定ref --><children-component :ref="getChildList"></children-component>...<script> import { ref } from 'vue' export default { setup () { // 2、父组件中定义和ref同名的变量 const getChildList = ref(null) onMounted (() => { // 4、调用子组件中的getList()办法 console.log(getChildList.value.getList()) }) return { // 3、return进来 getChildList } } })</script>子组件 setup () { // 办法 const getList = () => { console.log('子组件中的办法') } return { getList }}

June 8, 2021 · 1 min · jiezi

关于vue3:Vue3-UI-框架候选

PRIMVE VUEPrimeVue的创建者是PrimeTek Informatics,它是驰名的组件库供应商,在过来的几年中构建了风行的开源我的项目,例如PrimeFaces,PrimeNG和PrimeReact。 VARLET面向Vue3的Material格调挪动端组件库

June 4, 2021 · 1 min · jiezi

关于vue3:vue3-ts-获取组件-ref-实例

在 vue3 中获取组件的类型: type EleForm = InstanceType<typeof ElForm>在template中获取组件的ref <template> <ElForm ref="$form"></ElForm></template><script lang="ts">import { Options, Vue } from 'vue-class-component'import { ElForm } from 'element-plus'@Options<Home>({ components: { ElForm, },})export default class Home extends Vue { $form = ref<InstanceType<typeof ElForm>>(null) mounted() { this.$form?.value // 类型正确 }}</script>tsx等render组件中获取的形式更简略 import { defineComponent, ref, onMounted } from '@vue/runtime-core'import { ElForm } from 'element-plus'export default defineComponent({ setup() { const $form = ref<InstanceType<typeof ElForm>>(null) onMounted(() => { $form?.value // 类型正确 }) return () => <ElForm ref={$form}></ElForm> }})须要留神的是,如果应用expose裸露办法进来,无奈获取到对应的类型,您须要自定义类型https://github.com/vuejs/rfcs... ...

June 1, 2021 · 1 min · jiezi

关于vue3:头部置顶20

因为之前的头部置顶1.0存在一些问题。当头部上方还须要置顶一些其余货色时,比方导出案件、筛选框、搜寻框、题目等等时,之前的代码不够灵便,没有设置置顶的偏移量。当页面内关上抽屉反复利用table时,也会存在问题,因为里面的滚动曾经让头部置顶了,关上抽屉时,table的head仍然在,并且会遮挡数据。 因而重写了置顶的js代码,通过监听对应dom的滚动,而不是监听window。 // 判断是否呈现滚动条function hasScrolled(el: HTMLElement, direction = 'vertical') { if (direction === 'vertical') { return el.scrollHeight > el.clientHeight; } else if (direction === 'horizontal') { return el.scrollWidth > el.clientWidth; }}// 找到有滚动条的domfunction findScrollDom(el: HTMLElement) { if (!el) return null; const isScrolled = hasScrolled(el); if (isScrolled) { return el; } const pEl = el.parentElement; if (pEl) { return findScrollDom(pEl); } else { return null; }}function useListenerScroll() { const containerRef = ref(); const isFixed = ref(false); const width = ref('100%'); // 置顶容易的宽度 const top = ref('51px'); // 默认的定位高度,导航栏高度 const titleHeight = ref(0); // head上放的title或其余操作的容器高度 const initEvent = async () => { await nextTick(); // 如果table组件为空,递归调用 if (!containerRef.value) { initEvent(); return; } // 找到滚动容器 const scrollDom = findScrollDom(containerRef.value); if (scrollDom) { const titleDom = scrollDom.getElementsByClassName('table-title-content'); // 获取title内容高度 if (titleDom[0]) titleHeight.value = titleDom[0].clientHeight; else titleHeight.value = 0; scrollDom.addEventListener('scroll', listenerScroll); top.value = scrollDom.offsetTop + 'px'; } }; function listenerScroll(event: any) { const $el = event.target; if ($el) { const scrollTop = $el.scrollTop; const fixedDom = $el.getElementsByClassName('table-content'); if (fixedDom.length) { const dom = fixedDom[0]; const domTop = dom.offsetTop - titleHeight.value - 51; // 滚动高度大于设定高度时,置顶头部 if (scrollTop > domTop) { isFixed.value = true; } if (scrollTop < domTop) { isFixed.value = false; width.value = dom.clientWidth + 'px'; } } } } return { top, width, isFixed, containerRef, initEvent, };}<template> <div ref="containerRef"> <div :style="fixedHeaderStyle"> <div class="table-title-content" v-if="$slots['w1-title'] || title"> <span v-if="title">{{ title }}</span> <slot name="title" /> </div> <div> <a-table :pagination="false" v-bind="$attrs" :data-source="[]" > <template #[item]="data" v-for="item in Object.keys($slots)"> <slot :name="item" v-bind="data" /> </template> </a-table> </div> </div> <a-table class="table-content" v-bind="$attrs" :dataSource="dataSource" v-loading="loading" > <template #[item]="data" v-for="item in Object.keys($slots)"> <slot :name="item" v-bind="data"> </slot> </template> </a-table> </div></template><script lang="ts"> import { computed, defineComponent, onMounted } from 'vue'; import { isFunction } from '/@/utils/is'; import useListenerScroll from './hooks/useListenerScroll'; export default defineComponent({ props: { dataSource: { type: Array, }, loading: { type: Boolean, }, title: { type: String, }, }, setup(props) { const { isFixed, containerRef, initEvent, width, top } = useListenerScroll(); const fixedHeaderStyle = computed(() => ({ position: isFixed.value ? 'fixed' : 'relative', top: isFixed.value ? top.value : 0, zIndex: isFixed.value ? 19 : 0, width: width.value, })); onMounted(() => { initEvent(); }); return { isFixed, containerRef, fixedHeaderStyle, }; }, });</script>

May 28, 2021 · 2 min · jiezi

关于vue3:记录学习vuenext源码-reactivity

前言:为了学习vue-next源码,我不仅一遍做思维导图,还写了一个mini-vue库,该库目前只有reactivity局部,在库中还蕴含了相干的流程图等更不便了解原理,对于runtime-dom局部网上曾经有相干的demo了。 思维导图mini-vue-reactivity 提出几个疑难:细节的代码,都曾经在demo中做了正文。上面就一起来思考几个问题。学习就是一个提疑的过程。如果真的想从源码中学习更多的常识,品味代码中的细节也是很有意思的。一问,为什么proxy代理的对象,在拦挡办法中,是用Reflect原型拜访对象属性的? 二问,scheduler的实现,为什么只用promise就能够?而vue2中却setTimeout,setimmediate等? 三问,为什么watch的生效函数是通过runner函数的参数传入的,而不通过返回stop函数的参数传入呢? 四问,组件定义的watch到底需不需要咱们手动去stop呢? 五问,为什么scheduler中要定义前置缓冲队列,同步队列,后缀缓冲队列? 从源码中学到了优雅的命名高级函数weekmap的正确打开方式调度的实现

May 24, 2021 · 1 min · jiezi

关于vue3:vue3值得注意的点

创立实例通过createApp的形式<div id="counter"> Counter: {{ counter }}</div>const Counter = { data() { return { counter: 0 } }}Vue.createApp(Counter).mount('#counter')全局办法变更 组件的v-model在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接管抛出的 update:modelValue 事件: <ChildComponent v-model="pageTitle" /><!-- 简写: --><ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event"/>v-model 参数若须要更改 model 名称,而不是更改组件内的 model 选项,那么当初咱们能够将一个 argument 传递给 model: <ChildComponent v-model:title="pageTitle" /><!-- 简写: --><ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />v-model 修饰符除了像 .trim 这样的 2.x 硬编码的 v-model 修饰符外,当初 3.x 还反对自定义修饰符:增加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。在上面的示例中,咱们创立了一个组件,其中蕴含默认为空对象的 modelModifiers prop。请留神,当组件的 created 生命周期钩子触发时,modelModifiers prop 蕴含 capitalize,其值为 true——因为它被设置在 v-model 绑定 v-model.capitalize="bar"。 ...

May 23, 2021 · 2 min · jiezi

关于vue3:Vue3初体验

装置独立装置能够在Vue.js官网间接下载最新版本,并用script标签引入 独立装置 CDN形式装置间接应用script引入<script src="https://unpkg.com/vue@next"></script> npm形式装置npm版本需大于3.0 npm install vue@next 命令行工具: 从之前的版本包名扭转了,从vue-cli变为@vue/cli。如果之前已全局装置了vue-cli1.x或vue-cli2.x。首先须要 应用命令npm uninstall vue-cli -g或者yarn global remove vue-cli卸载掉之前的版本,在进行装置 Node版本留神点: Vue CLI 4.x 须要NodeJs的版本>=8.9 npm install -g @vue/cli 或者 yarn global add @vue/cli 留神:vue-cli 3.x 和 vue-cli 2.x 应用了雷同的 vue 命令,如果你之前曾经装置了 vue-cli 2.x,它会被替换为 Vue-cli 3.x。装置 @vue/cli-int: npm i -g @vue/cli-init 创立我的项目Vue CLI应用命令vue create 项目名称来创立我的项目 而后期待下载对应的模板以及依赖。 运行: cd 我的项目名 npm run serve ViteVite 是一个 web 开发构建工具,因为其原生 ES 模块导入形式,能够实现闪电般的冷服务器启动。 通过在终端中运行以下命令,能够应用 Vite 疾速构建 Vue 我的项目。 ...

May 23, 2021 · 10 min · jiezi

关于vue3:在-vue-3-中使用-markdown-编辑器

装置# 应用 npmnpm i @kangc/v-md-editor@next -S# 应用yarnyarn add @kangc/v-md-editor@next引入组件import { creatApp } from 'vue';import VMdEditor from '@kangc/v-md-editor';import '@kangc/v-md-editor/lib/style/base-editor.css';import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';import '@kangc/v-md-editor/lib/theme/style/github.css';VMdEditor.use(githubTheme);const app = creatApp(/*...*/);app.use(VMdEditor);根底用法<template> <v-md-editor v-model="text" height="400px"></v-md-editor></template><script>import { ref } from 'vue';export default { setup () { const text = ref(''); return { text } }}</script>保留后的 markdown 文本如何渲染在页面上?如果你的我的项目中引入了编辑器。你能够间接应用编辑器的预览模式来渲染。例如: 渲染 markdown 文本如果你的我的项目中引入了编辑器。你能够间接应用编辑器的预览模式来渲染。例如: <template> <v-md-editor :value="markdown" mode="preview"></v-md-editor></template><script>export default { data() { return { markdown: '### 题目', }; },};</script>如果你的我的项目不须要编辑性能,只须要渲染 markdown 你能够只引入 preview 组件来渲染。例如: ...

May 19, 2021 · 1 min · jiezi

关于vue3:toRaf和ref的区别

先来说说ref吧ref创立时,须要给它一个指定的值类型数据,这个值类型数据能够是具体某个值类型也能够是某对象属性外面的值类型 例如: let obj ={name: 'znq'}let state =ref(obj.name)批改ref的数据须要通过.value的模式来批改例如: state.value='李小龙'实质上ref函数是将某一对象的属性变成响应式数据,但对象的属性还是原来那样,所以咱们通过 state.value批改数据,但对象obj.name的值还是'znq',所以咱们能够说ref是通过复制来应用值类型的 当初来说说toRef吧toRef大体上跟ref一样,不同的是通过toRef将某一对象的属性变成响应式数据,接着通过.value批改响应式数据,原数据obj内的属性也会跟着产生扭转, 是不是有点像援用类型的数据啊(●∀●) 还有个最大的区别是 通过toRef创立的数据被批改后并不会触发ui视图的更新 我写的都是重点

May 18, 2021 · 1 min · jiezi

关于vue3:封装弹窗抽屉使用hook函数形式减少template内部代码

hook模式应用弹窗组件vue3新增了composition api之后,组件性能的拆分更加不便了,上面应用这种形式来编写弹窗组件,抽屉同理。 MyDrawer.vue首先,自定义一个dialog组件。此组件外部裸露管制弹窗显隐的办法、和设置属性的办法,getCurrentInstance是vue提供的获取组件实例的办法,具体代码如下——— <template> <el-dialog :before-close="()=>{ actionDialog(false) }" v-model="visible" v-bind="getProps" > <template #[item]="data" v-for="item in Object.keys($slots, 'default')"> <slot :name="item" v-bind="data"></slot> </template> </el-dialog></template><script lang="ts">import { computed, defineComponent, getCurrentInstance, reactive, ref,} from "vue";export default defineComponent({ name: "", setup(props, { emit }) { const visible = ref(false); const propsRef = reactive({ visible: false, title: undefined, }); const getProps = computed(() => { return Object.assign(propsRef, props); }); const dialogInstance = { actionDialog, setProps, }; const instance = getCurrentInstance(); if (instance) { emit("register", dialogInstance); } function actionDialog(isShow: boolean) { visible.value = isShow; } function setProps(propsValue: any) { Object.assign(propsRef || {}, propsValue); } return { visible, getProps, actionDialog, }; },});</script>useDialog.ts除了自定义组件外部须要做一些解决之外,还须要提供一个裸露一些操作弹窗办法的useDialog办法,这就是常说的hook函数吧。此处裸露一个register办法,在应用时该办法须要绑定在自定义dialog组件上。当创立了dialog组件后,就会触发register办法,将dialog实例和外部提供的办法传递进去,而后在父组件中应用。 ...

May 6, 2021 · 2 min · jiezi

关于vue3:在Vue-3中使用typescript和jsx

本文将介绍如何在Vue 3中应用tsx 首先答复两个问题: Why Vue 3在Vue 2中,组件相干的逻辑都被写在一个文件中,常常会呈现一个组件几千行代码,十分难以保护;而且一个性能的相干代码通常会扩散写在data methods created等各个选项中,想要复用一个性能也是比拟艰难的。而Vue 3的组合式API正是为了解决这个问题。 Why tsxVue中的组件是一个黑盒,你不晓得他的内部结构(属性和事件),只能通过文档甚至浏览源码来理解,这样是很消耗工夫的。而在tsx中,在编写代码时就能够取得代码提醒,组件的内部结构能够高深莫测,且具备代码约束力(当你编写了谬误的代码时会取得一个报错),这样是能够极大的进步代码编写效率,缩小低级谬误呈现。 组件的类型申明Props ts会从vue组件的props申明中推断属性的类型,以下是一些根本的类型推断: String → stringNumber → numberBoolean → booleanArray → unknown[]Object → Record<string, any>Date → stringFunction → FunctionSymbol → symbol你能够通过PropType来进行更加准确的类型申明,倡议对所有的Array Object Function都进行申明 import { defineComponent, PropType } from 'vue'export default defineComponent({ props: { title: String, values: { type: Array as PropType<number[]>, required: true, }, data: Object as PropType<{ id: number, name: string }>, onClick: Function as PropType<(id: number) => void> }})属性默认是可选的,在下面例子中的title默认会失去一个string | undefined的类型。如果你申明了required: true,他的类型就会变成string。 ...

April 30, 2021 · 3 min · jiezi

关于vue3:vue30网易云音乐及入门小案例

前言之前学习vue3.0时做了几个小案例,近段时间又找了几个案例想着把之前做的那些和当初做的放在一起于是劳动的时候就搭建了一个vue3的我的项目。这个我的项目所用的数据都是页面上写死的,音乐播放器数据是调用的网易云api拿到的。指标性能[√] 手机、邮箱登录[√] 批改明码[√] 我的歌单信息[√] 最近播放[√] 搜寻性能[√] 搜寻后果展现[√] 发现页面歌单[√] 发现页面排行榜[√] 喜爱音乐(获取用户喜爱的音乐)[x] 歌曲喜爱与否[√] 歌词[√] 相干评论[x] 评论点赞 (目前只获取评论点赞数量,没有做点赞性能)[√] 歌曲mv播放[√] 签到[√] 注册[√] 歌手分类 登录页面,原本想做个滑块验证,然而之前vue2用到的vue-monoplasty-slide-verify插件如同不反对vue3,所以这一块就还没做。登录调用的是网易云登录接口,目前只做了手机号、邮箱登陆,二维码登录接口始终不返回数据临时先不做影视大厅性能还没有做,豆瓣的API当初曾经不能用了,等找到其余API再做这部分。局部截图1.登陆页面 2.首页 3.批改明码 4.举荐页 5.历史歌单 6.歌曲mv播放 启动步骤查看源码点击 查看源码 欢送star,欢送issuenpm installnpm run dev前期我会在博客更新我的项目开发过程中遇到的坑,学习到的新常识,新办法等。欢送关注 本我的项目会长期更新,欢送大家指出问题,独特学习心愿对读完本文的你有帮忙、有启发,如果有不足之处,欢送批评指正交换!辛苦整顿好久,还望手动点赞激励~

April 27, 2021 · 1 min · jiezi

关于vue3:最简vue3

疯狂的 Vue3只用四个函数, 不须要相熟 JavaScript 各种奇怪的常识, 不须要简单的配置, 就能够立即写出比较复杂的前端页面. 一开始我也不置信, 但 Vue3 真的做到了. 而且很神奇的一点是, 在 Vue3 的架构里我发现了很多相似 elixir/erlang 的中央. createApp对于母语是 elixir/erlang 的程序员来说, App(lication) 是很相熟的概念: 狭义上值整个虚拟机中的一个根本独立的利用, 广义上指实现了 Application behaviour 的模块. Vue 中的 App 比 elixir/erlang 的更加独立一些, 但实现形式很相似, 只有实现一个对应的回调函数即可. import { createApp, h } from 'vue'const hello = (props, context) => h('div', null, 'hello vue3')createApp(hello).mount('#app')等价 html: <div>hello vue3</div>App 的回调函数须要的返回值是一个 vnode, 即 vue 中虚构的 html dom元素; 另外这个函数会传入两个参数, 其中 property 即是该 vnode 所接管的属性, 例如 <div id="app"></div> 外面的 id 就是一个属性. context 则提供了一系列该 vnode 可用的 vue 内置办法. ...

April 21, 2021 · 2 min · jiezi

关于vue3:Vue3使用vuecli搭建项目

一、Vue3简介2020年9月,Vue.js3.0'One Piece'正式版公布,vue3反对vue2大多数个性,并更好的反对Typescript.性能方面,相比Vue2,vue3的性能晋升了许多 打包大小缩小41%首次渲染快55%,更新渲染快133%内存缩小54%应用Proxy代替defineProterty实现数据响应式重写虚构DOM的实现和Tree-Shaking(精简模块,去掉未应用过的)二、应用vue-cli创立vue3我的项目1、查看vue-cli版本,必须在4.5.0以上 vue -V2、若版本过低,需降级(Vue CLI由原来的vue-cli改成了@vue/cli。若曾经全局装置了旧版本的vue-cli须要通过npm uninstall vue-cli -g卸载) npm install -g @vue/cli3、创立我的项目 vue create my-project 抉择第三个,按Enter 而后抉择TypeScript,按空格选中。(临时不选vue-router和vuex)按Enter 选3.x之后的始终Enter创立实现,cd到创立的我的项目门路下,输出npm run serve开启一个node服务 4、我的项目简介 我的项目构造 main.ts//程序的主入口文件,(ts文件)//引入createApp函数,创立对应的利用,产生利用的实例对象import { createApp } from 'vue'//引入App组件(所有组件的父级组件)import App from './App.vue'//创立App利用,返回对应的实例对象,调用mount办法挂载到id为app的DOM中(public文件夹下的index.html)createApp(App).mount('#app')App.vue<template><!-- Vue2中的html模板必须要有一对根标签,Vue3组件的html模板中能够没有根标签 --> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/></template><!--script标签能够设置lang为ts,反对ts代码--><script lang="ts">// defineComponent函数,作用是定义一个组件import { defineComponent } from 'vue';import HelloWorld from './components/HelloWorld.vue';//应用defineComponent()对外裸露一个定义好的组件//函数外部能够传入一个配置对象,对象与vue2统一export default defineComponent({ name: 'App', //以后组件的名字 components: { //注册组件 HelloWorld //注册一个子组件 }});</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> ...

April 19, 2021 · 1 min · jiezi

关于vue3:基于vue3的小型图书管理项目

前言Vue3 练手我的项目,为了加深对 composition-api 的了解,我的项目参考于 sl1673495/vue-bookshelf,不过这个我的项目还是基于 vue2+composition-api,外面对于组合函数的应用和了解还是很有帮忙的,这里用 Vue3 做了批改。 我的项目地址:vue-bookshelf 我的项目中会用到的 Vue3 api,你须要在开始之前对它们有所理解: [x] Provide / Inject[x] ref、reactive、watch、computed[x] directive[x] 生命周期函数[x] v-model 多选项绑定provide/inject代替vuexVue3 中新增的一对api,provide 和 inject,能够很不便的治理利用的全局状态,有趣味能够参考下这篇文章:Vue 3 store without Vuex 官网文档对 Provide / Inject 的应用阐明:Provide / Inject 利用这两个api,在没有vuex的状况下也能够很好的治理我的项目中的全局状态: import { provide, inject } from 'vue'const ThemeSymbol = Symbol()const Ancestor = { setup() { provide(ThemeSymbol, 'dark') }}const Descendent = { setup() { const theme = inject(ThemeSymbol, 'light' /* optional default value */) return { theme } }}开始我的项目介绍我的项目很简略,次要逻辑如下: ...

March 25, 2021 · 2 min · jiezi

关于vue3:vue3响应式原理

vue3 响应式原理的实现,源码地址:vue3_reactivity rollup 搭建ts环境装置 rollup 插件npm install rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-replace rollup-plugin-serve typescript -D包名性能rollup打包工具rollup-plugin-typescript2解析ts插件@rollup/plugin-node-resolve解析第三方模块@rollup/plugin-replace替换插件rollup-plugin-serve启动本地服务插件配置打包环境生成 tsconfig.json 文件: npx tsx --init批改 tsconfig.json 配置属性 module 为 ESNext(默认在浏览器运行) 能够设置 strict 属性为false,让 ts 反对 any 类型,scouceMap 须要设置成 true,不便调试代码根目录新建 rollup.config.js 配置文件,并输出上面内容: import path from "path";import ts from "rollup-plugin-typescript2";import { nodeResolve } from "@rollup/plugin-node-resolve";import replace from "@rollup/plugin-replace";import serve from "rollup-plugin-serve";export default { input: 'src/index.ts', output: { name: 'VueReactivity', format: 'umd', file: path.resolve('dist/VueReactivity.js'), sourcemap: true, }, plugins: [ nodeResolve({ extensions: ['.js', '.ts'], }), ts({ tsconfig: path.resolve(__dirname, 'tsconfig.json'), }), replace({ "process.env.NODE_ENV": JSON.stringify("development"), }), serve({ open: true, openPage: "/public/index.html", port: 3000, contentBase: "" }) ],}新建入口文件srx/index.ts和模板文件public/index.html。 ...

March 19, 2021 · 11 min · jiezi

关于vue3:vue30脚手架创建项目

进入vue3.0官网文档 第一种办法:CLI 注:我不喜爱大量的全局装置各种依赖,这样容易导致环境抵触,所以没有应用官网文档的装置形式,而是应用的npx,这个命令真好用 //1.首先npx装置@vue/cli,防止了全局装置@vue/cli,留神我的项目名不能蕴含大写字母npx install @vue/cli create project-test-vue3//2.敲回车键之后会给你抉择,倡议抉择Manually select features,能够手动抉择本人想要装置的依赖,能够抉择装置ts第二种办法:Vite npm init vite-app <project-name>

March 16, 2021 · 1 min · jiezi

关于vue3:在vue3vite项目中使用svg

明天在vue3+vite我的项目练习中,在应用svg时,发现之前的写法不能用,之前的应用办法参考:vue2中优雅的应用svg const req = require.context('./icons/svg', false, /\.svg$/)const requireAll = requireContent => requireContent.keys().map(requireContent)requireAll(req)而后就各种材料查找,终于实现了,废话不多说,间接上代码: stept1: 文件目录 stept2: 装置 svg-sprite-loadernpm install svg-sprite-loader -D# via yarnyarn add svg-sprite-loader -Dstept3: 创立svgIcon.vue文件 <template> <svg :class="svgClass" v-bind="$attrs" :style="{color: color}"> <use :xlink:href="iconName"/> </svg></template><script setup>import { defineProps, computed } from "vue";const props = defineProps({ name: { type: String, required: true }, color: { type: String, default: '' }})const iconName = computed(()=>`#icon-${props.name}`);const svgClass = computed(()=> { console.log(props.name, 'props.name'); if (props.name) { return `svg-icon icon-${props.name}` } return 'svg-icon'});</script><style lang='scss'>.svg-icon { width: 1em; height: 1em; fill: currentColor; vertical-align: middle;}</style>stept4: 创立icons文件夹,寄存svg文件stept5: 在main.js外面全局注入svg-icon组件import { createApp } from 'vue'import App from './App.vue'import svgIcon from './components/svgIcon.vue'createApp(App).component('svg-icon', svgIcon).mount('#app');stept6: 在plugins文件夹创立svgBuilder.js(重点来了), ts版本参考:https://github.com/JetBrains/svg-sprite-loader/issues/434import { readFileSync, readdirSync } from 'fs'let idPerfix = ''const svgTitle = /<svg([^>+].*?)>/const clearHeightWidth = /(width|height)="([^>+].*?)"/gconst hasViewBox = /(viewBox="[^>+].*?")/gconst clearReturn = /(\r)|(\n)/gfunction findSvgFile(dir) { const svgRes = [] const dirents = readdirSync(dir, { withFileTypes: true }) for (const dirent of dirents) { if (dirent.isDirectory()) { svgRes.push(...findSvgFile(dir + dirent.name + '/')) } else { const svg = readFileSync(dir + dirent.name) .toString() .replace(clearReturn, '') .replace(svgTitle, ($1, $2) => { // console.log(++i) // console.log(dirent.name) let width = 0 let height = 0 let content = $2.replace( clearHeightWidth, (s1, s2, s3) => { if (s2 === 'width') { width = s3 } else if (s2 === 'height') { height = s3 } return '' } ) if (!hasViewBox.test($2)) { content += `viewBox="0 0 ${width} ${height}"` } return `<symbol id="${idPerfix}-${dirent.name.replace( '.svg', '' )}" ${content}>` }) .replace('</svg>', '</symbol>') svgRes.push(svg) } } return svgRes}export const svgBuilder = (path, perfix = 'icon') => { if (path === '') return idPerfix = perfix const res = findSvgFile(path) // console.log(res.length) // const res = [] return { name: 'svg-transform', transformIndexHtml(html) { return html.replace( '<body>', ` <body> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0"> ${res.join('')} </svg> ` ) } }}stept7: 最初在vite.config.js批改配置import { svgBuilder } from './src/plugins/svgBuilder'; export default defineConfig({ plugins: [svgBuilder('./src/icons/svg/')] // 这里曾经将src/icons/svg/下的svg全副导入,无需再独自导入})

February 22, 2021 · 2 min · jiezi

关于vue3:ElementPlusViteStarterPnpm版本

1 起因因为最近Vite降级了2.x版本,我的项目中须要改变的货色有点多,原本想基于官网给出的starter重做,然而又看到了一个叫pnpm的仓库,构建速度会比原生npm/yarn快两倍以上: 因而模拟官网starter做了一个pnpm版本的starter,心愿能帮忙到须要的同学。 2 环境筹备Node.jsnpmpnpmNode.js与npm的装置就不说了,原本笔者应用的是cnpm,尽管速度上相比起npm有所改进,而且cnpm的输入也更加敌对,然而应用了pnpm,相比起来感觉还是差了那么一点。 装置pnpm之前,能够先把cnpm卸载(当然也能够抉择不卸载): npm uninstall -g cnpm再设置一下淘宝镜像: npm config set registry https://registry.npm.taobao.org而后装置pnpm(笔者零碎Manjaro,aur曾经提供了,能够间接yay装置): yay -S pnpm# 也能够应用npm装置npm install -g pnpm测试: pnpm -v应用pnpm命令时,只需替换原生的npm命令即可,比方应用 pnpm install去代替 npm install同理npx的代替品是pnpx。 3 初始化基于Vite Getting Started文档,输出 pnpm init @vitejs/app接着输出我的项目名字,并抉择模板: 默认提供的模板如图所示,抉择实现后即可。 也能够一步创立实现: pnpm init @vitejs/app my-vue-app --template vue接着装置依赖: pnpm install pnpm install --save element-plus这样就实现了初始化工作,我的项目构造如下: 4 引入ElementPlus依照ElementPlus文档引入,批改main.js如下: import { createApp } from 'vue'import App from './App.vue'import ElementPlus from 'element-plus'import 'element-plus/lib/theme-chalk/index.css'createApp(App).use(ElementPlus).mount('#app')将以下图片笼罩logo.png: ...

February 16, 2021 · 1 min · jiezi

关于vue3:Vue30

之前曾经理解过了vue2.0版本,当初咱们来理解一下3.0版本.首先咱们来看下变动: 源码组织形式的变动.Composition API性能晋升Vite首先源码的组织形式首先类型束缚2.0是flow而后3.0源码采纳TypeScript重写而后就是应用Monorepo治理我的项目构造3.0 2.0 而后就是Composition API设计动机: 回顾OptionsAPI蕴含一个形容组件选项(data、methods、props等)的对象OptionsAPI开发简单组件,同一个性能逻辑的代码被拆分到不同选项 随着业务复杂度越来越高,代码量会一直的加大;因为相干业务的代码须要遵循option的配置写到特定的区域,导致后续保护十分的简单,代码可复用性也不高 CompositionAPIVue.js3.0新增的一组API一组基于函数的API能够更灵便的组织组件的逻辑 通过compostion API显然咱们能够更加优雅的组织咱们的代码,函数。让相干性能的代码更加有序的组织在一起 setup函数vue3中专门为组件提供的新属性.它为咱们应用vue3的Composition API 新特新提供了对立的入口.之前定义的data,methods等都对立放入setup中实现。 setup函数会在beforeCreate之后、created之前 所以它外部的this指向window, 接管外界传入的props,承受的组件必须props里须要申明否则拿不到传值. //home.vue里 < setup : data = "123" ></setup >//setup.vue里export default { setup (props) { console.log(props.data) }, props: { data: Number }}在新版的生命周期函数,能够按需导入到组件中,且只能在setup()函数中应用. import { onMounted, onUnmounted } from 'vue';export default { setup () { onMounted(() => { // }); onUnmounted(() => { // }); }};性能晋升响应式系统升级编译优化源码体积的优化vue2.x中响应式零碎的外围defineProperty vue3.0中应用Proxy对象重写响应式零碎 能够监听动静新增的属性能够监听删除的属性能够监听数组的索引和length属性不须要一个一个侵入对象递归劫持属性,而是间接代理对象编译优化:2.x中通过标记动态根节点,优化diff的过程 3.0中标记和晋升所有的动态根节点,diff的时候只须要比照动静节点内容 Fragments(降级vetur插件)动态晋升Patchflag缓存事件处理函数优化代码打包体积,Vue.js3.0中移除了一些不罕用的API,例如:inline-template、filter等,而后就是Tree-shaking 摇树优化 ...

January 23, 2021 · 1 min · jiezi

关于vue3:用NodeJs创建一个Vue3项目

作者说:通过浏览以下文章内容并实际操作,能够帮忙你疾速创立一个Vue3我的项目。Step1:创立一个文件夹,例如在C盘下创立一个名为vue的文件夹 Step2:关上cmd(windows自带的命令提示符工具),进入你在上一步创立的文件夹cd C:/vue 而后输出以下命令,初始化vite-vue我的项目npm init @vitejs/app my-vue-app --template vue Step3:初始化实现之后,进行装置依赖操作npm install Step4:运行我的项目npm run dev Step5:关上浏览器,输出启动地址就能够开始欢快的Vue3入坑之旅了~http://localhost:3000/#/ 非必选Step:如果须要装置vue-router vuex等依赖,在我的项目文件夹下运行以下代码:npm install vue-router@nextnpm install vuex@next 扩大浏览Vite是Vue的开发者尤雨溪开发的新工具,它能够疾速搭建一个开发服务器,可用于代替基于Webpack的开发方式,提供一种新的前端开发体验。 Vite基于浏览器的ES Modules实现,在工作时,它将拦挡无关ES模块的申请(import申请),应用浏览器的<script module> 进行转换,而后通过<script>发动HTTP申请,获取并解析相应的模块代码。 在此过程中,Vite省去了Webpack的捆绑和打包过程,能够提供更快的热更新速度。喜爱折腾的敌人能够尝新一下~ 相干文档Vue3文档Vite文档

January 5, 2021 · 1 min · jiezi

关于vue3:Vue3Vite引入Echarts50图表库

1 概述环境Vue3+Vite,须要引入ECharts库。 2 尝试目前ECharts已更新到5.0版本,在Vue中引入并不难,npm/cnpm装置后在须要的组件中引入: import echarts from 'echarts'即可。 但, 问题是这是以前的版本可行的,更新到5.0版本后须要应用其余办法。 另一方面官网文档是应用require引入: 然而,这是在Webpack的状况下,在Vite中并不能间接应用require,官网issue有探讨,明说了require不反对,这是一个Node的个性,倡议应用import: 3 解决方案先装置: npm install --save echarts#或cnpm install --save echarts装置后在须要应用的组件中应用import引入: import * as echarts from 'echarts'...mounted(){ var myChart = echarts.init(document.getElementById('main')) //...}这样就能失常应用了。 最重要的就是将以前的 import echarts from 'echarts'改为 import * as echarts from 'echarts'

January 4, 2021 · 1 min · jiezi

关于vue3:vue3-自定义hook之useEmitter

前言vue3新个性,hooks钩子函数,用过react hooks的应该很相熟,思维是统一的,学习老本也不高,跟一般的函数相比,最大的特点就是能再函数中应用组件/页面的一些生命周期了,这时你应该会马上想到vue2的mixin函数,然而hooks会比mixin更加灵便,且副作用更小。vue3组件传值除了emit和props,举荐provide/injeect,store。跨组件传值曾经不能像之前一样new Vue(),因为实例上的$on、$off 和 $once 实例办法被删除了。所以我找了他的代替计划:miit: Tiny 200b functional event emitter / pubsub.即小型性能事件发射接管,相似vue2版本中的$emit、$on npm install --save mitt在组件层级简单的状况下,这十分有必要,且集体认为,应用频率较高,该需要出险频率十分高附上链接:https://www.npmjs.com/package... 前置知识点getCurrentInstance: 获取以后组件的实例,顺便提一句,vue3不反对操作prototype一样去挂载全局办法,倡议应用getCurrentInstance().appContext.config.globalProperties拜访全局属性getCurrentInstance 只能在 setup 或生命周期钩子中调用。 需要在组件开发中,不仅有子组件须要调用父组件的场景,也有场景是相同的,父组件须要调用子组件的计划,vue3+ts如果你间接ref.xxx拜访子组件的办法或报错,因为它并不知道你的子组件中有什么办法,有人间接把整个子组件$emit给父组件,而后调用他的属性,不举荐此计划,不思考性能,我感觉很繁琐。那么,咱们实现的emitter就必须要有以下的根底性能: 向上发送事件(dispatch)向下发送事件(broadcast)只调用一次工夫(once)接管事件(on)销毁事件(off)代码实function on(type, handler) { const handleWrapper = (e) => { const { value, type, emitComponentInstance } = e if (type === BROADCAST) { // 监听是否是播送事件 if (isChildComponent(currentComponentInstance, emitComponentInstance)) { // 播送即以后接管的组件是子组件时,因为播送是从向往下 handler && handler(...value) } } else if (type === DISPATCH) { // 监听是dispatch,以后接管的组件是父组件 if (isChildComponent(emitComponentInstance, currentComponentInstance)) { // 判断以后组件是否是发送事件组件的子组件的判断逻辑 handler && handler(...value) } } else { handler && handler(...value) } } // 把接管到的handler保存起来,因为须要off掉,留神这个handler必要放弃惟一 ,function是援用类型,传递给off的时候,必须找到雷同的地址才行。这里的`wrapper`变量能够应用`Symbol`类型 handler[wrapper] = handleWrapper emitter.on(type, handleWrapper) }function broadcast(type, ...args) { emitter.emit(type, { type: BROADCAST, // 事件类型,辨别第一个参数,第一个参数是事件名称 emitComponentInstance: currentComponentInstance, // 以后组件实例 value: args }) }//逻辑同上,工夫类型不同function dispatch(type, ...args) { emitter.emit(type, { type: DISPATCH, emitComponentInstance: currentComponentInstance, value: args }) }// off掉的是惟一雷同的handler!function off(type, handler) { emitter.off(type, handler[wrapper])}// 执行一次的原理就是调用后即off掉function once(type, handler) { const handleOn = (...args) => { handler && handler(...args) off(type, handleOn) } on(type, handleOn)}残缺代码import { getCurrentInstance } from 'vue'import mitt from 'mitt'const DISPATCH = 'dispatch'const BROADCAST = 'broadcast'const wrapper = Symbol('wrapper')const emitter = mitt()export function useEmitter() { const currentComponentInstance = getCurrentInstance() function on(type, handler) { const handleWrapper = (e) => { const { value, type, emitComponentInstance } = e if (type === BROADCAST) { if (isChildComponent(currentComponentInstance, emitComponentInstance)) { handler && handler(...value) } } else if (type === DISPATCH) { if (isChildComponent(emitComponentInstance, currentComponentInstance)) { handler && handler(...value) } } else { handler && handler(...value) } } // Save the real handler because the need to call off handler[wrapper] = handleWrapper emitter.on(type, handleWrapper) } function broadcast(type, ...args) { emitter.emit(type, { type: BROADCAST, emitComponentInstance: currentComponentInstance, value: args }) } function dispatch(type, ...args) { emitter.emit(type, { type: DISPATCH, emitComponentInstance: currentComponentInstance, value: args }) } function off(type, handler) { emitter.off(type, handler[wrapper]) } function once(type, handler) { const handleOn = (...args) => { handler && handler(...args) off(type, handleOn) } on(type, handleOn) } return { on, broadcast, dispatch, off, once }}/** * check componentChild is componentParent child components * @param {*} componentChild * @param {*} componentParent */function isChildComponent(componentChild, componentParent) { const parentUId = componentParent.uid while (componentChild && componentChild?.parent?.uid !== parentUId) { componentChild = componentChild.parent } return Boolean(componentChild)}根本应用const { dispatch, on, broadcast } = useEmitter()on('update:modelValue', (v) => { emit('update:modelValue', v) dispatch('custom.value.change', v)})broadcast('fieldReset', 'reset')

January 4, 2021 · 2 min · jiezi

关于vue3:vue30自定义弹窗组件vue3-pc端对话框vue3layer

介绍Vue3-Layer 一款基于vue3.x实现的PC桌面端弹窗组件。领有极简的调用形式、超多的参数配置。反对拖拽、缩放、最大化、全局、自定义弹窗款式等性能。 疾速引入// 在main.js中全局引入组件import { createApp } from 'vue'import App from './App.vue'const app = createApp(App)// 引入Element-Plus组件库import ElementPlus from 'element-plus'import 'element-plus/lib/theme-chalk/index.css'// 引入弹窗组件v3layerimport Vue3Layer from './components/v3layer'app.use(ElementPlus)app.use(Vue3Layer)app.mount('#app')v3layer同样反对标签式+函数式两种调用形式。 标签式调用<v3-layer v-model="showDialog" title="题目内容" content="<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>" z-index="1011" lockScroll="false" xclose resize dragOut :btns="[ {text: '勾销', click: () => showDialog=false}, {text: '确认', style: 'color:#f90;', click: handleSure}, ]"/> <template v-slot:content>这里是自定义插槽内容信息!</template></v3-layer>函数式调用let $el = v3layer({ title: '题目内容', content: '<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>', shadeClose: false, zIndex: 1011, lockScroll: false, xclose: true, resize: true, dragOut: true, btns: [ {text: '勾销', click: () => { $el.close() }}, {text: '确认', click: () => handleSure}, ]});当弹窗类型为 message | popover | notify 则调用如下: ...

January 3, 2021 · 5 min · jiezi

关于vue3:基于Vue30自定义移动端弹层组件Vue3Popup

介绍V3Popup 一款基于vue3.0开发手机端弹出框组件。在开发设计之初参考借鉴了Vant3及Antdv2.0中弹框组件化思维。反对超过20+种参数灵便配置。 引入组件// 在main.js中全局引入import { createApp } from 'vue'import App from './App.vue'// 引入弹窗组件v3popupimport V3Popup from './components/v3popup' createApp(App).use(V3Popup).mount('#app')遵循极简的调用形式,反对组件式+函数式两种调用。 组件式<!-- 提示框 --><v3-popup v-model="showMsg" anim="fadeIn" content="msg提示框测试(3s后窗口敞开)" shadeClose="false" time="3" /> <!-- 询问框 --><v3-popup v-model="showConfirm" shadeClose="false" title="题目" xclose z-index="2020" content="<div style='color:#1be769;padding:20px;'>确认框(这里是确认框提示信息,这里确认框提示信息,这里是确认框提示信息)</div>" :btns="[ {text: '勾销', click: () => showConfirm=false}, {text: '确定', style: 'color:#f90;', click: handleInfo}, ]"/>函数式let $el = this.$v3popup({ title: '题目', content: '<div style='color:#f90;padding:10px;'>这里是内容信息!</div>', type: 'android', shadeClose: false, xclose: true, btns: [ {text: '勾销', click: () => { $el.close(); }}, {text: '确认', style: 'color:#f90;', click: () => handleOK}, ], onSuccess: () => {}, onEnd: () => {}})vue2中能够通过prototype原型链挂载全局办法。vue3如果挂载全局办法呢?其实vue3中提供了两种形式来挂载全局办法。 和 两种形式。1、通过app.config.globalProperties.$v3popup = V3Popup ...

December 30, 2020 · 4 min · jiezi

关于vue3:vue3typeScript-手风琴每周一个小组件

每周一个小组件前言实现性能:带切换动画成果的手风琴每周分享一个vue3+typeScript的小组件,我只想分享下本人的实现思路,楼主是个菜鸡前端,记录下实现过程,说不定对你有帮忙。 成果展现预览地址 github地址 开发过程思路:点击手风琴题目传入它的索引,定义一个参数来接管点击的索引,这个参数等于索引就显示手风琴内容。自定义内容能够依据索引来动态显示。 html局部<div class="accordion"> <div v-for="(vo,inx) in items" :key="inx"> <!-- 手风琴题目 --> <div class="item" @click="changeItem(vo,inx)"> {{vo.title}} </div> <!-- 手风琴内容 --> <div class="content" v-show="active==inx&&vo.show"> {{vo.content}} <!-- 能够自定义内容构造 --> <div v-if="inx===0"> 我是自定义内容1 </div> <div v-if="inx===1"> 我是自定义内容2 </div> <div v-if="inx===2"> 我是自定义内容3 </div> </div> </div></div>ts局部<script lang="ts">import { defineComponent, reactive, toRefs} from 'vue'export default defineComponent({ setup() { const data = reactive({ items: [{ title: "JavaScript", content: "这是内容1", show: true }, { title: "Java", content: "这是内容2", show: true }, { title: "C++", content: "这是内容3", show: true } ], active: 0, changeItem: (vo: any, inx: number) => { //如果反复点击一个栏目item 能够敞开和关上以后栏目手风琴内容 if (inx === data.active) { vo.show = !vo.show } else { vo.show = true } data.active = inx } }) return { ...toRefs(data) } }})</script>css局部.accordion { width: 800px; padding: 50px 20px; background: #ecf0f3; height: 600px; .item { text-align: center; line-height: 80px; margin: 0 auto; width: 600px; height: 80px; border-radius: 12px; box-shadow: inset 12px 12px 20px #d1d9e6, inset -12px -12px 20px #fff; cursor: pointer; margin-bottom: 5px; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .content { opacity: 0; min-height: 80px; width: 600px; margin: 0 auto; animation-name: fadeIn; animation-duration: 1s; animation-fill-mode: both; }}vue3继续更新中... ...

December 29, 2020 · 1 min · jiezi

关于vue3:vue30自定义指令drectives

在大多数状况下,你都能够操作数据来批改视图,或者反之。然而还是防止不了偶然要操作原生 DOM,这时候,你就能用到自定义指令。 举个例子,你想让页面的文本框主动聚焦,在没有学习自定义指令的时候,咱们可能会这么做。 const app = Vue.createApp({ mounted(){ this.$refs.input.focus(); }, template: `<input type="text" ref="input" />`,}); 在mounted钩子函数里,通过 $refs 获取须要聚焦的 DOM 元素,而后调用 focus 办法实现主动聚焦的性能。 根本应用下面做法曾经实现了咱们须要的性能,然而如果说咱们有多个组件都须要这个性能,那咱们只能把这段代码复制过来,从新实现逻辑。咱们上面看下如果应用自定义指令,应该怎么做。 const app = Vue.createApp({ // 通过 v-[自定义指令名称] 绑定自定义指令 template: `<input type="text" v-focus/>`,});// 注册一个全局自定义指令app.directive('focus',{ // 当被绑定的元素插入到DOM的时候执行.. mounted(el){ el.focus(); }}) 如上,咱们定义了一个全局自定义指令 focus,并通过 v-focus 绑定到须要聚焦的 input 元素上。如果,其余组件或模块也须要聚焦性能,只有简略的绑定此指令即可。 自定义指令的钩子函数咱们在下面定义指令的时候,会发现其中蕴含了 mounted 钩子函数,指令还提供了如下钩子函数,咱们用代码的模式来给大家列出来。 app.directive('directiveName', { // 指令绑定元素挂载前 beforeMount(el) {}, // 指令绑定元素挂载后 mounted(el, binding) {}, // 指令绑定元素因为数据批改触发批改前 beforeUpdate(el) {}, // 指令绑定元素因为数据批改触发批改后 updated(el) {}, // 指令绑定元素销毁前 beforeUnmount(el) {}, // 指令绑定元素销毁后 unmounted(el) {},}); 成果就不一一列举了,有趣味大家能够尝试下别离触发这些钩子函数。指令钩子函数大部分与组件很相似,大家能够比照着来看。 ...

December 26, 2020 · 1 min · jiezi

关于vue3:Vue30对比Vue20的不同之处

1.v-model语法糖废除,改用 modelValue <input v-model="value" /><input modelValue="value" />2.弃用全局API new Vue ,应用 createApp const app = Vue.createApp({})3.弃用Vue.prototype,在Vue3中,咱们能够应用如下定义形式 const app = Vue.createApp({})app.config.globalProperties.$http = () => {}4.全局办法当初全副在app实例上,例如: `app.directive`,`app.use`等5.当初你须要手动挂载根节点 app.mount("#app")6.不能再应用Vue.nextTick/this.$nextTick,Vue3中你能够用: import { nextTick } from 'vue'nextTick(() => { // something})7.Vue3容许template设置key。 8.正式弃用scopedSlots正式弃用,旧的不去新的不来。 9.监听数组变动须要应用deep属性,否则只能监听到整个数组被替换。 10.弃用$children,拜访子组件能够应用$ref 11.filter被移除,我X,不能再应用|了。 12.移除事件API,$on,$once,$off不再应用。EventBus办法也不再应用。

December 21, 2020 · 1 min · jiezi

关于vue3:Vue3源码整理及备注

reactive办法 export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (target && (target as Target)[ReactiveFlags.IS_READONLY]) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers )}创立响应式对象办法 function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // target already has corresponding Proxy const proxyMap = isReadonly ? readonlyMap : reactiveMap const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // only a whitelist of value types can be observed. const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy}

December 20, 2020 · 1 min · jiezi

关于vue3:vue3-vite-typescript-eslint-jest-项目配置实践

vue3 + vite + typescript + eslint + jest 我的项目配置实际1. 我的项目初化# 全局装置vite-appnpm i -g vite-app# 创立我的项目yarn create vite-app <project-name># 或者npm init vite-app <project-name># 进入我的项目,装置依赖cd <project-name>yarn # 或 npm i# 运行我的项目yarn dev # 关上浏览器 http://localhost:3000 查看2. 引入TypeScript# 退出ts依赖yarn add --dev typescript在 我的项目根目录下创立typescript的配置文件 tsconfig.json { "compilerOptions": { // 容许从没有设置默认导出的模块中默认导入。这并不影响代码的输入,仅为了类型查看。 "allowSyntheticDefaultImports": true, // 解析非绝对模块名的基准目录 "baseUrl": ".", "esModuleInterop": true, // 从 tslib 导入辅助工具函数(比方 __extends, __rest等) "importHelpers": true, // 指定生成哪个模块零碎代码 "module": "esnext", // 决定如何解决模块。 "moduleResolution": "node", // 启用所有严格类型查看选项。 // 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, // --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。 "strict": true, // 生成相应的 .map文件。 "sourceMap": true, // 疏忽所有的申明文件( *.d.ts)的类型查看。 "skipLibCheck": true, // 指定ECMAScript指标版本 "target": "esnext", // 要蕴含的类型申明文件名列表 "types": [ ], "isolatedModules": true, // 模块名到基于 baseUrl的门路映射的列表。 "paths": { "@/*": [ "src/*" ] }, // 编译过程中须要引入的库文件的列表。 "lib": [ "ESNext", "DOM", "DOM.Iterable", "ScriptHost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx" ], "exclude": [ "node_modules" ]}在 src 目录下新加 shim.d.ts 文件 ...

December 18, 2020 · 3 min · jiezi

关于vue3:关于-vue3-中删除-ononceoff-API-后的替代方案

举荐一个仓库。地址:https://github.com/tangdaohai... 在 vue3 版本中删除了 $on/$once/$off API (see),不过不必放心,能够应用此仓库作为代替计划,持续应用 event bus 的形式来实现跨组件的通信性能,并且不必手动去 $off 事件回调。 vue-happy-bus是一款基于vue3实现的订阅/公布插件。 在 vue 中,咱们能够应用 event bus 来实现 跨组件间通信。但一个弊病就是,这种形式并不会主动销毁,所以为了防止回调函数反复执行,还要在 onUnmounted 中去移除回调函数。 这样带来的冗余代码就是: $on 的回调函数必须是具名函数。不能简略的 $on('event name', () => {}) 应用匿名函数作为回调,因为这样无奈销毁事件监听,所以个别采纳 具名函数 作为回调在onUnmounted生命周期中去销毁事件的监听。我只是想在某个路由中监听下 header 中一个按钮的点击事件而已,居然要这么麻烦??? 所以此轮子被造出来了 。 它次要解决在夸组件间通信时,防止反复绑定事件、无奈主动销毁的而导致回调函数被执行屡次的问题。 总得来说他是能让你偷懒少写代码的工具。 具体应用文档见此处),不过不必放心,能够应用此仓库作为代替计划,持续应用 event bus 的形式来实现跨组件的通信性能,并且不必手动去 $off 事件回调。 vue-happy-bus是一款基于vue3实现的订阅/公布插件。 在 vue 中,咱们能够应用 event bus 来实现 跨组件间通信。但一个弊病就是,这种形式并不会主动销毁,所以为了防止回调函数反复执行,还要在 onUnmounted 中去移除回调函数。 这样带来的冗余代码就是: $on 的回调函数必须是具名函数。不能简略的 $on('event name', () => {}) 应用匿名函数作为回调,因为这样无奈销毁事件监听,所以个别采纳 具名函数 作为回调在onUnmounted生命周期中去销毁事件的监听。我只是想在某个路由中监听下 header 中一个按钮的点击事件而已,居然要这么麻烦??? ...

November 19, 2020 · 1 min · jiezi

关于vue3:vue3快速入手-起始和新特性解读

装置vitenpx create-vite-app myvue3-appnpm installnpm run dev composition apicomposition api 为vue代码提供更好的逻辑复用和代码组织 <template><div> <h1>{{ msg }}</h1> <button @click="count++">count is: {{ count }}</button> <p>Edit <code>components/HelloWorld.vue</code> to test hot module replacement.</p> <p>{{counter}}</p> <p>{{doubleCounter}}</p> <p>{{msg2}}</p> <div ref="desc"></div></div></template><script>import { reactive, ref, toRefs, computed, watch, onMounted, onUnmounted} from 'vue'export default { name: 'HelloWorld', props: { msg: String }, setup(props) { const { counter, doubleCounter } = useCounter(); const msg2 = ref('message'); // 应用元素援用 const desc = ref(null); watch(counter, (val, oldVal) => { const p = desc.value; p.textContent = `counter change from ${oldVal} to ${val}` }) return { counter, doubleCounter, msg2, desc } }}function useCounter() { // counter相干 const data = reactive({ counter: 1, doubleCounter: computed(() => data.counter * 2) }) let timer onMounted(() => { timer = setInterval(() => { data.counter++ }, 1000) }) onUnmounted(() => { clearInterval(timer) }) return toRefs(data)}</script>Teleport传送门组件提供一种简洁的形式能够指定它外面内容的父元素 ...

October 19, 2020 · 2 min · jiezi

关于vue3:Vue30-composition-api-特性学习

options-api首先咱们来看看 option-api。 default export { data() { return { state:0 } }, methods:{ myFun() { console.log(this.state) } }}下面代码的形式就是 option-api, 也是 vue2.x 中最罕用的形式。 composition-api在vue3.x中,能够应用上面的 composition-api 形式。 default export { setup(){ const state = ref(0) function myFunOne() { console.log(state.value) } function myFunTwo() { console.log(state.value + 1) } return { state, myFunOne } }} 解决痛点看上面的图片(起源:https://composition-api.vuejs.org/#code-organization),雷同业务的代码色彩雷同,能够看见 Options API 的雷同业务代码扩散在各处,这样前期保护起来就会十分麻烦,而Composition-Api 解决了这个痛点。 知识点梳理对于 composition-api 所蕴含的函数有以上这么多,上面依据相干例子一个个来学习。 setupsetup性能是新的组件选项。它是组件外部应用Composition-API的入口。 可选参数它有两个可选参数 props 和 context。 这两个参数能够解构应用。 props 和 vue2.x props是一样的,外面的属性是 父组件传给自组子或 vue-router 传给 页面的参数。context具备与vue2.x 中 this.$attrs,this.$slots,this.$emit,this.$parent,this.$root对应的属性。 ...

October 5, 2020 · 3 min · jiezi

关于vue3:深度解读-Vue3-源码-内置组件-teleport-是什么来头

前言 上一篇文章,咱们讲了「Vue3」 runtime 和 compile 联合的 patch 过程。仿佛,因为文章内容太过艰涩的起因,并没有收到很多同学的反馈。然而,其实这里我想说的是源码就是这样,初见时如陌生人个别,再见时如初恋,既相熟又思念。 所以,这一篇文章,我打算讲个「Vue3」中轻松愉快的设计点——内置组件 teleport。那么,这次咱们将会从应用角度和源码角度去深刻理解 teleport 组件是如何实现的? 什么是 teleport 组件当然,如果曾经懂得怎么应用 teleport 组件的同学能够跳过这个大节。咱们从应用性的角度思考,很事实的一点,就是 teleport 组件能带给咱们什么价值? 最经典的答复就是开发中应用 Modal 模态框的场景。通常,咱们会在中后盾的业务开发中频繁地应用到模态框。可能对于中台还好,它们会搞一些 low code 来缩小开发成本,但这也是个别大公司或者技术较强的公司能力实现的。 而理论状况下,咱们传统的后盾开发,就是会存在频繁地手动应用 Modal 的状况,它看起来会是这样: <div class="page"> <div class="header">我心愿点击我呈现弹窗</div> <!--假如此处有 100 行代码--> .... <Modal> <div> 我是 header 心愿出的弹窗 </div> </Modal></div>这样的代码,凸显进去的问题,就是脱离了所见即所得的理念,即我头部心愿呈现的弹窗,因为款式的问题,我须要将 Modal 写在最上面。 而 teleport 组件的呈现,首当其冲的就是解决这个问题,依然还是下面那个栗子,通过 teleport 组件咱们能够这么写: <div class="page"> <div class="header">我心愿点击我呈现弹窗</div> <!--弹窗内容--> <teleport to="#modal-header"> <div> 我是 header 心愿出的弹窗 </div> </teleport> <!--假如此处有 100 行代码--> .... <Modal id="modal-header"> </Modal></div>联合 teleport 组件应用 modal,一方面,咱们的弹窗内容,就能够合乎咱们的失常的思考逻辑。并且,另一方面,也能够充沛地进步 Modal 组件的可复用性,即页面中一个 Modal 负责展现不同内容。 ...

September 27, 2020 · 3 min · jiezi

关于vue3:Vue30-Vite-使用-Bootstrap

应用 Vite 构建 Vue3.0 我的项目并尝试应用 Bootstrap 这里不会去写 如何应用 Vite 如何构建 Vue3.0 我的项目 这个能够去看 Vite 的 Readme在尝试应用 Bootstrap发现 npm i Bootstrap npm 不会将 Bootstrap 的外部依赖 Jquery popper.js 也一起装置导致一连报错 正确形式$ npm i bootstrap jquery popper.js再在 main.ts 增加 import "jquery";import "bootstrap";import "bootstrap/dist/css/bootstrap.min.css";

August 11, 2020 · 1 min · jiezi

关于vue3:Vue30-Typescript-使用-VueuseHighlight-问题记录

在应用 Vue3.0 试验我的项目中 对我的项目中试验的应用 highlightjs 并以自定义指令的模式来写 其中遇到 用 Typescript 写 Vue3.0 的 Vue.use() 的问题 已在 npm i highlight.js 的状况下 先定义 Vue 的内部插件highlight.ts import Vue from 'vue';import Hljs from 'highlight.js';import 'highlight.js/styles/monokai-sublime.css'interface Highlightjs {[x:string]:any}let Highlight:Highlightjs = {};// let Highlight:any = {};Highlight.install = function (Vue:any,options:any) { Vue.directive('highlightA',{ inserted:function(el:any){ let blocks = el.querySelectorAll('per code'); for (let i = 0; i < blocks.length; i++) { const item = blocks[i] Hljs.highlightBlock(item) } } }) Vue.directive('highlightB',{ componentUpdated:function(el:any){ let blocks = el.querySelectorAll('per code'); for (let i = 0; i < blocks.length; i++) { const item = blocks[i] Hljs.highlightBlock(item) } } })}export default Highlight在 main.ts 引入import { createApp } from 'vue'import App from './App.vue'import './index.css'import router from './router';import Highlight from './lib/typescript/highlight';const app = createApp(App)app.use(router)app.use(Highlight)app.mount('#app')error ...

August 11, 2020 · 1 min · jiezi