- [前沿探索]VUE 3.0 Beta前瞻---尤雨溪直播学习笔记
[图片]4月21日,VUE作者尤雨溪在B站上分享了3.0 Beta版本最新的改进动向,作为和React、Angular一起的全球主流的框架,在很多地方都有应用,它的一些改动也代表着前端技术一个演变的方向~相信大家开发小程序的时候也会觉得和它有几分的相像,Virtual Dom 作为目前主流view层展示的技术思想,有很多值得我们学习的,知识往往是触类旁通的。 这里我整理了下来自 juejin掘金 @前端劝退师 发表的一片文章,也是我目前看到写的比较好的~ 1. 全新文档[代码]RFCs[代码][图片] [代码]Vue.js 3.0 Beta[代码]发布后的工作重点是保证稳定性和推进各类库集成 所有的进度和文档都将在全新[代码]RFCs[代码]文档可以看到。 2. 六大亮点[图片] [代码]Performance[代码]:性能更比[代码]Vue 2.0[代码]强。[代码]Tree shaking support[代码]:可以将无用模块“剪辑”,仅打包需要的。[代码]Composition API[代码]:组合[代码]API[代码][代码]Fragment, Teleport, Suspense[代码]:“碎片”,[代码]Teleport[代码]即[代码]Protal传送门[代码],“悬念”[代码]Better TypeScript support[代码]:更优秀的Ts支持暴露了自定义渲染[代码]API[代码]下面将按顺序分别描述。 3. [代码]Performance[代码][图片] 重写了虚拟[代码]Dom[代码]的实现(且保证了兼容性,脱离模版的渲染需求旺盛)。编译模板的优化。更高效的组件初始化。[代码]update[代码]性能提高1.3~2倍。[代码]SSR[代码]速度提高了2~3倍。下面是各项性能对比 [图片] 要点1:编译模板的优化[图片] 假设要编译以下代码 <!--html --> <div> <span/> <span>{{ msg }}</span> </div> 将会被编译成以下模样: import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue" export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("span", null, "static"), _createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */) ])) } // Check the console for the AST 注意看第二个[代码]_createVNode[代码]结尾的“1”: Vue在运行时会生成[代码]number[代码](大于0)值的[代码]PatchFlag[代码],用作标记。 [图片] 仅带有[代码]PatchFlag[代码]标记的节点会被真正追踪,且无论层级嵌套多深,它的动态节点都直接与[代码]Block[代码]根节点绑定,无需再去遍历静态节点 再看以下例子: [图片] <!--html --> <div> <span>static</span> <span :id="hello" class="bar">{{ msg }} </span> </div> 会被编译成: import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue" export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("span", null, "static"), _createVNode("span", { id: _ctx.hello, class: "bar" }, _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["id"]) ])) } [代码]PatchFlag[代码] 变成了[代码]9 /* TEXT, PROPS */, ["id"][代码] 它会告知我们不光有[代码]TEXT[代码]变化,还有[代码]PROPS[代码]变化(id) 这样既跳出了[代码]virtual dom[代码]性能的瓶颈,又保留了可以手写[代码]render[代码]的灵活性。 等于是:既有[代码]react[代码]的灵活性,又有基于模板的性能保证。 要点2: 事件监听缓存:[代码]cacheHandlers[代码][图片] 假设我们要绑定一个事件: <!--html --> <div> <span @click="onClick"> {{msg}} </span> </div> 关闭[代码]cacheHandlers[代码]后: import { toDisplayString as _toDisplayString, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue" export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("span", { onClick: _ctx.onClick }, _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["onClick"]) ])) } [代码]onClick[代码]会被视为[代码]PROPS[代码]动态绑定,后续替换点击事件时需要进行更新。 开启[代码]cacheHandlers[代码]后: import { toDisplayString as _toDisplayString, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue" export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("span", { onClick: _cache[1] || (_cache[1] = $event => (_ctx.onClick($event))) }, _toDisplayString(_ctx.msg), 1 /* TEXT */) ])) } [代码]cache[1][代码],会自动生成并缓存一个内联函数,“神奇”的变为一个静态节点。 Ps:相当于[代码]React[代码]中[代码]useCallback[代码]自动化。 并且支持手写内联函数: <!--html --> <div> <span @click="onClick"> {{msg}} </span> </div> 补充:[代码]PatchFlags[代码]枚举定义而通过查询[代码]Ts[代码]枚举定义,我们可以看到分别定义了以下的追踪标记: export const enum PatchFlags { TEXT = 1,// 表示具有动态textContent的元素 CLASS = 1 << 1, // 表示有动态Class的元素 STYLE = 1 << 2, // 表示动态样式(静态如style="color: red",也会提升至动态) PROPS = 1 << 3, // 表示具有非类/样式动态道具的元素。 FULL_PROPS = 1 << 4, // 表示带有动态键的道具的元素,与上面三种相斥 HYDRATE_EVENTS = 1 << 5, // 表示带有事件监听器的元素 STABLE_FRAGMENT = 1 << 6, // 表示其子顺序不变的片段(没懂)。 KEYED_FRAGMENT = 1 << 7, // 表示带有键控或部分键控子元素的片段。 UNKEYED_FRAGMENT = 1 << 8, // 表示带有无key绑定的片段 NEED_PATCH = 1 << 9, // 表示只需要非属性补丁的元素,例如ref或hooks DYNAMIC_SLOTS = 1 << 10, // 表示具有动态插槽的元素 // 特殊 FLAGS ------------------------------------------------------------- HOISTED = -1, // 特殊标志是负整数表示永远不会用作diff,只需检查 patchFlag === FLAG. BAIL = -2 // 一个特殊的标志,指代差异算法(没懂) } 感兴趣的可以看源码:[代码]packages/shared/src/patchFlags.ts[代码] 4. [代码]Tree shaking support[代码][图片] 可以将无用模块“剪辑”,仅打包需要的(比如[代码]v-model,<transition>[代码],用不到就不会打包)。一个简单“[代码]HelloWorld[代码]”大小仅为:13.5kb11.75kb,仅[代码]Composition API[代码]。包含运行时完整功能:[代码]22.5kb[代码]拥有更多的功能,却比[代码]Vue 2[代码]更迷你。很多时候,我们并不需要 [代码]vue[代码]提供的所有功能,在 [代码]vue 2[代码] 并没有方式排除掉,但是 3.0 都可能做成了按需引入。 5. [代码]Composition API[代码][图片]与[代码]React Hooks[代码] 类似的东西,实现方式不同。 可与有的 [代码]Options API[代码]一起使用灵活的逻辑组合与复用[代码]vue 3[代码]的响应式模块可以和其他框架搭配使用混入([代码]mixin[代码]) 将不再作为推荐使用, [代码]Composition API[代码]可以实现更灵活且无副作用的复用代码。 感兴趣的可以查看:composition-api.vuejs.org/#summary [图片] [代码]Composition API[代码]包含了六个主要[代码]API[代码] 可以这里查看:composition-api.vuejs.org/api.html#se… Ps:其它的均为常见的工具函数,可先忽略不看。 6. [代码]Fragment[代码][图片] [代码]Fragment[代码]翻译为:“碎片” 不再限于模板中的单个根节点[代码]render[代码] 函数也可以返回数组了,类似实现了 [代码]React.Fragments[代码] 的功能 。'[代码]Just works[代码]' 6.1 [代码]<Teleport>[代码][图片] 以前称为[代码]<Portal>[代码],译作传送门。更多细节将由[代码]@Linusborg [代码]分享[代码]<Teleport>[代码]原先是对标 [代码]React Portal[代码](增加多个新功能,更强) 但因为[代码]Chrome[代码]有个提案,会增加一个名为[代码]Portal[代码]的原生[代码]element[代码],为避免命名冲突,改为[代码]Teleport[代码] 6.2 [代码]<Suspense>[代码][图片] [代码]Suspense[代码]翻译为:“悬念” 可在嵌套层级中等待嵌套的异步依赖项支持[代码]async setup()[代码]支持异步组件虽然[代码]React 16[代码]引入了[代码]Suspense[代码],但直至现在都不太能用。如何将其与异步数据结合,还没完整设计出来。 [代码]Vue 3 [代码]的[代码]<Suspense>[代码]更加轻量: 仅5%应用能感知运行时的调度差异,综合考虑下,Vue3 的[代码]<Suspense>[代码] 没和[代码]React[代码]一样做运行调度处理 7. 更好的[代码]TypeScript[代码]支持[图片] [代码]Vue 3[代码]是用[代码]TypeScript[代码]编写的库,可以享受到自动的类型定义提示[代码]JavaScript[代码]和[代码]TypeScript[代码]中的API是相同的。事实上,代码也基本相同支持[代码]TSX[代码][代码]class[代码]组件还会继续支持,但是需要引入[代码]vue-class-component@next[代码],该模块目前还处在 alpha 阶段。还有[代码]Vue 3 + TypeScript[代码] 插件正在开发,有类型检查,自动补全等功能。目前进展可喜。 [图片] 8. [代码]Custom Renderer API[代码]:自定义渲染器API[图片] 正在进行[代码]NativeScript Vue[代码]集成用户可以尝试[代码]WebGL[代码]自定义渲染器,与普通Vue应用程序一起使用([代码]Vugel[代码])。意味着以后可以通过 [代码]vue[代码], [代码]Dom[代码] 编程的方式来进行 [代码]webgl[代码] 编程 。感兴趣可以看这里:Getting started vugel 9. 剩余工作[图片] 9.1 [代码]Docs & Migration Guides[代码][图片] 新的文档编写交由[代码]@NataliaTepluhina, @sdras, @bencodezen & @phanan[代码] 负责[代码]@sdras[代码] 正在做自动升级迁移工具[代码]@sodatea[代码] 已经开始研究[代码]CodeMods[代码] 9.2 [代码]Router[代码][图片] 下一代 [代码]Router:vue-router@next[代码]已在[代码]alpha[代码]阶段,感谢[代码]@posva[代码]有部分[代码]API[代码]变动,可到[代码]RFC[代码]上看。 9.3 [代码]Vuex[代码][图片] 下一代[代码]Vuex[代码]:,[代码]vuex@next[代码](与[代码]Vue 3 compat[代码]相同的API),已在[代码]alpha[代码]阶段,感谢[代码]@KiaKing[代码]。团队正在为下一次迭代试验[代码]Vuex API的简化[代码]目前以兼容[代码]Vue 3[代码]为主,基本上没有[代码]API[代码]变动,莫慌。 9.4 [代码]CLI[代码][图片] [代码]CLI[代码]插件:[代码]vue-cli-plugin-vue-next[代码]by [代码]@sodatea[代码](wip)[代码]CodeMods[代码]支持升级[代码]Vue 2[代码]应用9.5 新工具:[代码]vite[代码](法语 “快”)[图片] 地址:github.com/vuejs/vite 一个简易的[代码]http[代码]服务器,无需[代码]webpack[代码]编译打包,根据请求的[代码]Vue[代码]文件,直接发回渲染,且支持热更新(非常快) 9.6 [代码]vue-test-utils[代码][图片] 下一代[代码]test-utils:test-utils@next[代码]by [代码]@lmiller1990, @dobromir-hristov, @afontcu & @JessicaSachs[代码]9.7 [代码]DevTools[代码][图片] 早期的原型已经由@Akryum 完成,当我们到[代码]beta[代码]时,将完全集成。目前需要花更多精力去完善。 10. [代码]Vue 2.x[代码]还有2.7版本[图片] 将有最后一个小版本(2.7)从[代码]Vue 3[代码]向后移植兼容的改进(不损坏兼容性前提下)加上在[代码]Vue 3[代码]中删除的功能的弃用警告[代码]LTS1[代码] 18个月。 个人观后感总结: >>> VUE3.0可以密切关注,但在没有成熟方案前谨慎升级原有的2.X! 当我们跨入前端的那一刻,全栈开发已成为必然的趋势,技术的迭代越来越快,我们疲于奔命学习的同时,也要应该放慢脚步去思考下一些主流方案的由来,这样未来的有一天,我们或许将不再仅仅是工具/框架的使用者,而是他们的创造者。 看完觉得有帮助记得点个赞哦~你的赞是我继续分享的最大动力!^-^
2020-04-23 - input组件处于聚焦时,修改value导致触发bindinput的解决方案
在安卓机型下,input组件处于聚焦时,修改value会导致触发bindinput 解决思路如下(不是具体代码) // 定义一个loading变量 var loading = false // 修改value时 changeValue() { loading = true //... } // 触发bindinput时,限制其执行 bindInput() { if (loading) return //... } // 触发bindfocus时,重置loading bindfocus() { loading = false }
2019-12-27 - [开盖即食]小程序Canvas官方新版API实战
[图片] [图片] 最近本人在开发一个新项目的时候,注意到官方在2.9.0开始支持了一个canvas 2D的新API,同时对webGL上支持也有了很大的改进,相信很多人用canvas的组件做一些分享海报,战绩和新闻帖功能。 [图片] 这里是新的引入方式。 官方文档地址: https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html 那么新的canvas2D API有啥好处呢? 原本的API微信有做一定的修改,现在全面支持源生H5 JS的写法,迁移H5的老代码变成更加容易,学习成本更低 修复了一些诡异的BUG,例如原本在IOS早期版本写法顺序会导致clip()图片裁切失效等~ 性能上的优化和提升,复杂动画上帧数明显 举例写法上的一些改变: 1、设置font的写法: [代码]//原本(传值的写法) ctx.setFontSize(20); ctx.fillText('MINA', 100, 100) ctx.draw() //现在(和源生H5写法一致,赋值) ctx.font = "16px"; ctx.fillStyle = 'blue'; //可以直接写颜色,原本的不支持 //不需要 ctx.draw() [代码] 2、获取并添加图片写法: [代码]//原本 //使用的是 wx.getImageInfo的方法 wx.getImageInfo({ src: mainImg,//服务器返回的图片地址 success: function (res) { console.log(res); ctx.drawImage(res.path, 0, 0); ctx.draw(true); }, fail: function (res) { //失败回调 } }); //现在 //可以直接img.onload调用 const headerImg = canvas.createImage(); headerImg.src = headImage;//微信请求返回头像 headerImg.onload = () => { ctx.save(); ctx.beginPath()//开始创建一个路径 ctx.arc(38, 288, 18, 0, 2 * Math.PI, false)//画一个圆形裁剪区域 ctx.clip()//裁剪 ctx.drawImage(headerImg,0,0); ctx.closePath(); ctx.restore(); } [代码] 3、将canvas生成虚拟地址便于下载(重点): [图片] 由于官方文档没有写清楚,误导了挺多人的。这里canvas对象必须通过选择器获取,并获得对应的node节点。 [代码]async saveImg() { let self = this; //这里是重点 新版本的type 2d 获取方法 const query = wx.createSelectorQuery(); const canvasObj = await new Promise((resolve, reject) => { query.select('#posterCanvas') .fields({ node: true, size: true }) .exec(async (res) => { resolve(res[0].node); }) }); console.log(canvasObj); wx.canvasToTempFilePath({ //fileType: 'jpg', //canvasId: 'posterCanvas', //之前的写法 canvas: canvasObj, //现在的写法 success: (res) => { console.log(res); self.setData({ canClose: true }); //保存图片 wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (data) { wx.showToast({ title: '已保存到相册', icon: 'success', duration: 2000 }) // setTimeout(() => { // self.setData({show: false}) // }, 6000); }, fail: function (err) { console.log(err); if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { console.log("当初用户拒绝,再次发起授权") } else { util.showToast("请截屏保存分享"); } }, complete(res) { wx.hideLoading(); console.log(res); } }) }, fail(res) { console.log(res); } }, this) }, [代码] 分享个canvas海报的代码片段: [图片] 片段名: PoCf4emw7TgE 片段link: https://developers.weixin.qq.com/s/PoCf4emw7TgE [图片] [图片] 总结,相对之前还要看官方文档的canvas自定义API,现在写起来更加的方便,老代码迁移起来得心应手,只要你之前会canvas,那么各种效果和动画,拿来就怼,没什么大问题~ 一些奇怪的问题(注意!!!) canvas 2d 目前(2020年4月3日)还不支持真机调试,会报错!!! IDE工具 1.02.2003190 直接保存新版本canvas的API图片是打不开的,但是直接用手机保存在相册是没问题的,请更新到1.02.2003250 最新版即可解决~ 一些老款手机用新的API保存图片会有报错问题,如华为NOTE10,请更新系统到能支持的最新,且微信也是,即可解决~ 部分Android设备诡异的闪退和报错 [图片] 这种有可能是代码写法的问题,比如: [代码]//缺省写法 会导致部分Android机器 闪退 ctx.font = "bold 16px"; ctx.fillStyle = "#000" //在canvas 2D的写法中,所以写法必须规范且完整 ctx.font = "normal bold 12px sans-serif"; ctx.fillStyle = '#707070'; [代码] 所以在canvas 2D 的环境,所以写法必须原始且规范,不能用缺省写法,不然就会有诡异的闪退/报错。 后续:官方在7.0.13的Android版本已修复。 https://developers.weixin.qq.com/community/develop/doc/00088c13e1437890692afd8d85ec00 看完觉得有帮助记得点个赞哦~ 你的赞是我继续分享的最大动力!^-^
2020-05-09 - 教你怎么监听小程序的返回键
更新:2020年7月28日08:51:11 基础库2.12.0起,可以调用wx.enableAlertBeforeUnload监听原生右上角返回、物理返回以及wx.navigateBack时弹框提示 AIP详情请看: https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.enableAlertBeforeUnload.html //======================================== 怎么监听小程序的返回键? 应该有很多人想要监听用户的这个动作吧,但是很遗憾,小程序不会给你这个API的,那是不是就没辙了? 幸好我们还可以自定义导航栏,这样一来我们就可以监听用户的这一动作了。 什么?这你已经知道啦? 那好咱们就不说自定义导航栏的返回监听了,说一下物理返回和左滑?右滑?(不管了,反正是滑)返回上一页怎么监听。 监听物理返回 首先说一下这个监听方法的缺点,虽说是监听,但是还是无法真正意义上的监听并拦截来阻止页面跳转,页面还是会返回上一页,而后重新载入刚刚的页面,如果这不是你想要的,那可以不用往下看了 其次说一下用到什么东西: wx.onAppRoute、wx.showModal 最后是一些主要代码: 重写wx.showModal,主要是加个confirmStay参数和使wx.showModal Promise化 [代码]const { showModal } = wx; Object.defineProperty(wx, 'showModal', { configurable: false, // 是否可以配置 enumerable: false, // 是否可迭代 writable: false, // 是否可重写 value(...param) { return new Promise(function (rs, rj) { let { success, fail, complete, confirmStay } = param[0] param[0].success = (res) => { res.navBack = (res.confirm && !confirmStay) || (res.cancel && confirmStay) wx.setStorageSync('showBackModal', !res.navBack) success && success(res) rs(res) } param[0].fail = (res) => { fail && fail(res) rj(res) } param[0].complete = (res) => { complete && complete(res) (res.confirm || res.cancel) ? rs(res) : rj(res) } return showModal.apply(this, param); // 原样移交函数参数和this }.bind(this)) } }); [代码] 使用wx.onAppRoute实现返回原来的页面 [代码]wx.onAppRoute(function (res) { var a = getApp(), ps = getCurrentPages(), t = ps[ps.length - 1], b = a && a.globalData && a.globalData.pageBeforeBacks || {}, c = a && a.globalData && a.globalData.lastPage || {} if (res.openType == 'navigateBack') { var showBackModal = wx.getStorageSync('showBackModal') if (c.route && showBackModal && typeof b[c.route] == 'function') { wx.navigateTo({ url: '/' + c.route + '?useCache=1', }) b[c.route]().then(res => { if (res.navBack){ a.globalData.pageBeforeBacks = {} wx.navigateBack({ delta: 1 }) } }) } } else if (res.openType == 'navigateTo' || res.openType == 'redirectTo') { if (!a.hasOwnProperty('globalData')) a.globalData = {} if (!a.globalData.hasOwnProperty('lastPage')) a.globalData.lastPage = {} if (!a.globalData.hasOwnProperty('pageBeforeBacks')) a.globalData.pageBeforeBacks = {} if (ps.length >= 2 && t.onBeforeBack && typeof t.onBeforeBack == 'function') { let { onUnload } = t wx.setStorageSync('showBackModal', !0) t.onUnload = function () { a.globalData.lastPage = { route: t.route, data: t.data } onUnload() } } t.onBeforeBack && typeof t.onBeforeBack == 'function' && (a.globalData.pageBeforeBacks[t.route] = t.onBeforeBack) } }) [代码] 改造Page [代码]const myPage = Page Page = function(e){ let { onLoad, onShow, onUnload } = e e.onLoad = (() => { return function (res) { this.app = getApp() this.app.globalData = this.app.globalData || {} let reinit = () => { if (this.app.globalData.lastPage && this.app.globalData.lastPage.route == this.route) { this.app.globalData.lastPage.data && this.setData(this.app.globalData.lastPage.data) Object.assign(this, this.app.globalData.lastPage.syncProps || {}) } } this.useCache = res.useCache res.useCache ? reinit() : (onLoad && onLoad.call(this, res)) } })() e.onShow = (() => { return function (res) { !this.useCache && onShow && onShow.call(this, res) } })() e.onUnload = (() => { return function (res) { this.app.globalData = Object.assign(this.app.globalData || {}, { lastPage: this }) onUnload && onUnload.call(this, res) } })() return myPage.call(this, e) } [代码] 在需要监听的页面加个onBeforeBack方法,方法返回Promise化的wx.showModal [代码]onBeforeBack: function () { return wx.showModal({ title: '提示', content: '信息尚未保存,确定要返回吗?', confirmStay: !1 //结合content意思,点击确定按钮,是否留在原来页面,confirmStay默认false }) } [代码] 运行测试,Oj8K 是不是很简单,马上去试试水吧,效果图就不放了,静态图也看不出效果,动态图懒得弄,想看效果的自己运行代码片段吧 代码片段 https://developers.weixin.qq.com/s/hc2tyrmw79hg
2020-07-28