vue computed使用_vue 随记(3):“新时代”的姿势

89ff97f09571a3073977a0a03a36180d.png

体验vue3

对比2.x旧版本,vue3新增了什么:

•性能上:最多比vue2 快2倍•静态标记提升•proxy取代defineProperty•tree shaking:按需编译打包代码•composition api :类似hook的编码风格•支持typescript:面向未来目前的代码 98% 以上使用 TypeScript 编写。如果你还没有学习 TypeScript,请尽快学习,否则可能看不懂源码。另外有件事情说出来可能会让你非常惊讶,Vue 3 的源代码完全没有使用 class 关键字!(只在测试代码和示例代码里用到了 class 关键字)•custom renderer api:自定义渲染

1. 体验姿势

现在有三种姿势体验vue3。

1.1 脚手架工具

官方制定cli工具——升级最新版本。

npm install -g @vue/clivue create 01-vue3-clicd 01-vue3-clivue add vue-nextnpm run serve

bingo。

1.2 webpack

vue-cli一开始还没支持的时候,vue官网整了一个webpack的项目配置,直接clone即可

git clone https://github.com/vuejs/vue-next-webpack-preview.git 01-vue3-webpackcd 01-vue3-webpacknpm install npm run dev
1.3 代码仓库

可在此处克隆最新的仓库代码:https://github.com/vuejs/vue-next.git,下载下来之后运行dev命令打包:

 npm run dev

在example中新建一个vue3.html,直接引入vue.global.js

 lang="en">   charset="UTF-8">   name="viewport" content="width=device-width, initial-scale=1.0">  vue3  
id="app">
// ...
1.4 vite(划重点)

这是作者尤雨溪开发的新工具,目的是以后取代webpack,原理就是利用浏览器现在已经支持es6的import,碰见import会发送一个http请求去加载文件,vite拦截这些请求,做一些预编译,就省去了webpack冗长的打包时间,从而提升开发体验。(需要node 10.15以上版本)

npm install -g create-vite-appcreate-vite-app 01-vue3-vitecd 01-vue3-vitenpm installnpm run dev

打开http://localhost:3000,里面集成了一个vue 3小demo(没错又是计数器)

241b7b86878f9b4fad9ee3b7acd2c2b7.png

看下network,大概就知道vite的工作原理:以http请求的形式加载模块,这也是为什么它能做到复杂项目的秒开,天生的按需加载。

d2314fdc80780bfa58d2e38f274913d5.png

2. Options API 和 composition API

补白:模板fragement

其实fragement就是以后vue组件不需要一个根节点了,现在可以这么写template

    

哈喽
我真棒

用vue2的思路来写计数器,代码应该是这样的:

export default {  name: 'HelloWorld',  props: {    msg: String  },  data() {    return {      count: 0    }  },  methods:{    click(){      this.count += 1;    }  }}

但是,在vue3的时代,变了。修改app.vue,用vue的新语法改写下计数器:

  
alt="Vue logo" src="./assets/logo.png" />

Hello Vue 3.0 + Vite @click="click">count is: {{ state.count }} double is {{ state.double }}code>
import { reactive, watchEffect, computed } from "vue";export default { name: "App", setup() { // 定义响应式数据层,包括计算属性 const state = reactive({ count: 0, double: computed(() => state.count * 2), }); // 数据变化的副作用 watchEffect(() => { console.log(`数据变更为${state.count}`); }); const click = () => { state.count += 1; }; return { state, click, }; },};

有点像react hooks'的风格了。你每点击一次按钮,都会触发一次计数,并通过计算属性double返回视图层“双倍”信息。并且通过watchEffect,当数据变更时,在控制台打印出变更提示。

setup: 它只是一个函数,它将属性和函数返回到模板。可在此声明所有的响应式属性、计算属性、观察者和生命周期钩子,然后返回它们,以便它们可以在模板中使用。没有在setup函数返回的内容将在模板中不可用。•reactive:响应式,用的Proxy的getter和setter,取代Object.defineProperty。几乎等价于 2.x 中现有的 Vue.observable() API。这里返回的 state 是一个所有 Vue 用户都应该熟悉的响应式对象。•computed按需引入 tree-shaking生效->如果这里没有用到computed,vue3就会把它从打包中删掉。•useEffect:属性变更监听。watchEffect 应该接收一个应用预期副作用 (这里即设置 innerHTML) 的函数。它会立即执行该函数,并将该执行过程中用到的所有响应式状态的 property 作为依赖进行追踪。•这里的 state.count 会在首次执行后作为依赖被追踪。当 state.count 未来发生变更时,里面这个函数又会被重新执行。

这正是 Vue 响应式系统的精髓所在了!当你在组件中从 data() 返回一个对象,内部实质上通过调用 reactive() 使其变为响应式。而模板会被编译为渲染函数 ,因而可以使用这些响应式的 property。

实际上通过ref还可以再极简风一些:

import { ref, computed } from "vue";export default {  setup() {    // 初始化count为0    let count = ref(0);    const click = () => {      count.value += 1;    };    const double = computed(() => count.value * 2);    return {      count,      double,      click,    };  },};

关于 reactive 和 ref 的区别,目前可以这么理解:是把一个对象变为响应式,后者是把单独的一个值变为响应式。

类似第一种写法,被称为Options API(配置式API)。而第2/3种写法,被称为Compositon API(组合式API)。你甚至可以同时混用它们——虽然作者承诺,options API现在以后都不会废弃。但是组合式api看起来更像是一种更面向未来的vue语法。

组合式API是一组低侵入式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑。

——https://composition-api.vuejs.org/zh/

主要的改变在于:options API中诸如data/conputed/created等,到了都需要独立从vue对象中引入。能够很好的支持按需引入(tree shaking )。

在options API中,为了将逻辑添加到Vue组件中,我们一个个填充(options)属性,如data、methods、computed等。这种方法最大的缺点是,它本身不是一个工作的JavaScript代码。你需要确切地知道模板中可以访问哪些属性以及this关键字的行为——当项目需求变得越发复杂时,你就会在method,data,computed以及watch中“反复横跳”。在底层,Vue编译器需要将此属性转换为工作代码。正因为如此,我们无法从自动建议或类型检查中获益。(如下为一段OptionsAPI代码,新增/删除一处功能要改三四个地方,非常痛苦)

bf93fb4418ee300c457fdc286b1437c7.png

作为options api的替代方案,Composition API希望将通过当前组件属性、可用的机制公开为JavaScript函数来解决这个问题。Vue核心团队将组件Composition API描述为“一套附加的、基于函数的api,允许灵活地组合组件逻辑”。使用Composition API编写的代码更易读,并且场景不复杂,这使得阅读和学习变得更容易。(如图,setup中的每个数据,监听方法都被抽离出来了)

7ee2e87c9033d768b1c9dc55b7e09ddc.png

举个例子,我可以把所有内容封装为一个useCount函数:

import { ref, computed } from "vue";/*** initValue 初始值*/const useCount = (initValue) => {  // 初始化count为0  let count = ref(initValue);  const click = () => {    count.value += 1;  };  const double = computed(() => count.value * 2);  return {    count,    double,    click,  };};export default {  setup() {    const { count, double, click } = useCount(0);    return { count, double, click };  },};

完成了一个最简单的逻辑复用。useCount可以放到单独的文件进行管理。你还可以给useCount增加更多的逻辑:

import { ref, computed, onMounted, watchEffect } from "vue";const useCount = (initValue) => {  // 初始化count为0  let count = ref(initValue);  const click = () => {    count.value += 1;  };  const double = computed(() => count.value * 2);  onMounted(() => {    console.log("视图层渲染完毕");  });  watchEffect(() => {    console.log(`count内容被变更为${count.value}`);  });  return {    count,    double,    click,  };};

这也许是当前最好的逻辑复用解决方案(之前是根据mixin来做的)。

3. todoMVC实例

现在就用composition API写一个todoMVC。

3.1 原始需求

需求:写一个todoList ,实现增删查。

上面有输入框,下面有todos。可删除。

根据前面的示例可以很快写出

  

todoMVC type="text" v-model="state.addData" /> @click="add(state.addData)">add v-for="todo in state.list" v-bind:key="todo.id"> {{ todo.name }} >   >deletea >
import { ref, reactive, computed, onMounted, watchEffect } from "vue";export default { setup() { const state = reactive({ list: [ { id: 1, name: "aaa" }, { id: 2, name: "bbb" }, { id: 3, name: "ccc" }, ], addData: "", }); // 新增 const add = (value) => { const newData = { id: new Date().getTime(), name: value, }; state.list.push(newData); state.addData = ""; }; // 删除 const del = (id) => { let _i = ""; for (let i = 0; i < state.list.length; i++) { if (state.list[i].id == id) { _i = i; } } state.list.splice(_i, 1); }; return { state, add, del }; },};
3.2 加滚动需求

清单长度过长,滚动的时候输入框吸顶 ,这是一个比较常见的操作,如果options api 我们就用有两个解决方案

1.data里加个变量判断滚动,mounted和onUnmounted修改top,2.上述功能用mixin导入,缺点是来源不清晰,容易有重名bug

现在的思路是:

通过useScroll,定义top及其更新逻辑。当top小于60时,加上固定定位的classfixed

  
:class="{ fiexed: top > 60 }">

todoMVC type="text" v-model="state.addData" /> @click="add(state.addData)">add
v-for="todo in state.list" v-bind:key="todo.id"> {{ todo.name }} >   >deletea >
import { ref, reactive, computed, onMounted, onUnmounted, watchEffect,} from "vue";// 新增滚动const useScroll = (initVal) => { let top = ref(initVal); const update = () => { top.value = window.scrollY; }; onMounted(() => { window.addEventListener("scroll", update); }); onUnmounted(() => { window.removeEventListener("scroll", update); });return { top, };};export default { setup() { const state = reactive({ list: [ { id: 1, name: "aaa" }, { id: 2, name: "bbb" }, { id: 3, name: "ccc" }, { id: 4, name: "aaa" }, { id: 5, name: "bbb" }, { id: 6, name: "ccc" }, { id: 7, name: "aaa" }, { id: 8, name: "bbb" }, { id: 9, name: "ccc" }, ], addData: "", }); // 新增 const add = (value) => { const newData = { id: new Date().getTime(), name: value, }; state.list.push(newData); state.addData = ""; }; // 删除 const del = (id) => { let _i = ""; for (let i = 0; i < state.list.length; i++) { if (state.list[i].id == id) { _i = i; } } state.list.splice(_i, 1); }; const { top } = useScroll(0); return { state, add, del, top }; },};scope> li { height: 200px; } .fiexed { position: fixed; }

看到这个useScroll,完全可以从另外一个文件引入。管理起来相当轻松。

bd5b3da20002668edf1dd1bebe8cd1c8.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值