当前位置:   article > 正文

Vue3 核心功能的第一次尝鲜经历

test-dts

作者:你脑子里的铁线虫 链接:https://juejin.im/post/5e1451385188253ab54142e9

前言

vue 咱们都知道现在用的最多的是 2.x,但是众所周知在今年的上半年,至少作者是这么说的 所以很多东西也都还没确定百分百是这样的,很有可能有改动,这个到时候再说。

本章讲的内容基本都是根据 Vue 官方 RFC来写的,因为这里面的消息还相对可信。

与 vue 2.x 的区别以及个人观感

vue3给我的感觉有点像 vue2+mobx+ts+rxjs 的感觉,大家如果用过这些东西可能也会有这种感觉

不闲扯那些没用的,首先是生命周期 vue3舍弃了 beforeCreate 和 created,把这两个混在一起变成了一个setup , 其实在这里面还有一个核心概念,就是vue2.x 大家用过可能都明白,就是数据响应,以数据为中心

也就是observe。这个observe的优势大家都能感觉到,就是方便,劣势就是性能,因为他这个东西本质来说就是所有的东西我都监听,其实这在大型应用来说就比较不舒服了,因为你控制不了,而且功能也比较多,消耗也就高

react大家都知道,什么都不监听,除非你用第三方的比方说 mobx这种的,其他的都必须手动的去 setState

vue3就是在这两个方面折了一个中,这也是vue官方说的,既能够保证监听,又可以在大型应用上性能也是ok的,所以你需要自己去指定,这个要监听,那个不要监听


扯了那么多,首先咱们现在clone一下项目

  1. $ git clone https://github.com/vuejs/vue-next.git
  2. $ cd vue-next && yarn

克隆下来,然后安装,这时候需要改两个东西,都在项目的根目录 分别的 rollup.config.js

加上 output.sourcemap = true 这里已经改完了,需要的朋友可以直接复制

  1. import fs from 'fs'
  2. import path from 'path'
  3. import ts from 'rollup-plugin-typescript2'
  4. import replace from '@rollup/plugin-replace'
  5. import json from '@rollup/plugin-json'
  6. if (!process.env.TARGET) {
  7. throw new Error('TARGET package must be specified via --environment flag.')
  8. }
  9. const masterVersion = require('./package.json').version
  10. const packagesDir = path.resolve(__dirname, 'packages')
  11. const packageDir = path.resolve(packagesDir, process.env.TARGET)
  12. const name = path.basename(packageDir)
  13. const resolve = p => path.resolve(packageDir, p)
  14. const pkg = require(resolve(`package.json`))
  15. const packageOptions = pkg.buildOptions || {}
  16. const knownExternals = fs.readdirSync(packagesDir).filter(p => {
  17. return p !== '@vue/shared'
  18. })
  19. // ensure TS checks only once for each build
  20. let hasTSChecked = false
  21. const outputConfigs = {
  22. 'esm-bundler': {
  23. file: resolve(`dist/${name}.esm-bundler.js`),
  24. format: `es`
  25. },
  26. // main "vue" package only
  27. 'esm-bundler-runtime': {
  28. file: resolve(`dist/${name}.runtime.esm-bundler.js`),
  29. format: `es`
  30. },
  31. cjs: {
  32. file: resolve(`dist/${name}.cjs.js`),
  33. format: `cjs`
  34. },
  35. global: {
  36. file: resolve(`dist/${name}.global.js`),
  37. format: `iife`
  38. },
  39. esm: {
  40. file: resolve(`dist/${name}.esm.js`),
  41. format: `es`
  42. }
  43. }
  44. const defaultFormats = ['esm-bundler', 'cjs']
  45. const inlineFormats = process.env.FORMATS && process.env.FORMATS.split(',')
  46. const packageFormats = inlineFormats || packageOptions.formats || defaultFormats
  47. const packageConfigs = process.env.PROD_ONLY
  48. ? []
  49. : packageFormats.map(format => createConfig(format, outputConfigs[format]))
  50. if (process.env.NODE_ENV === 'production') {
  51. packageFormats.forEach(format => {
  52. if (format === 'cjs' && packageOptions.prod !== false) {
  53. packageConfigs.push(createProductionConfig(format))
  54. }
  55. if (format === 'global' || format === 'esm') {
  56. packageConfigs.push(createMinifiedConfig(format))
  57. }
  58. })
  59. }
  60. export default packageConfigs
  61. function createConfig(format, output, plugins = []) {
  62. if (!output) {
  63. console.log(require('chalk').yellow(`invalid format: "${format}"`))
  64. process.exit(1)
  65. }
  66. output.externalLiveBindings = false
  67. output.sourcemap = true
  68. const isProductionBuild =
  69. process.env.__DEV__ === 'false' || /\.prod\.js$/.test(output.file)
  70. const isGlobalBuild = format === 'global'
  71. const isRawESMBuild = format === 'esm'
  72. const isBundlerESMBuild = /esm-bundler/.test(format)
  73. const isRuntimeCompileBuild = /vue\./.test(output.file)
  74. if (isGlobalBuild) {
  75. output.name = packageOptions.name
  76. }
  77. const shouldEmitDeclarations =
  78. process.env.TYPES != null &&
  79. process.env.NODE_ENV === 'production' &&
  80. !hasTSChecked
  81. const tsPlugin = ts({
  82. check: process.env.NODE_ENV === 'production' && !hasTSChecked,
  83. tsconfig: path.resolve(__dirname, 'tsconfig.json'),
  84. cacheRoot: path.resolve(__dirname, 'node_modules/.rts2_cache'),
  85. tsconfigOverride: {
  86. compilerOptions: {
  87. declaration: shouldEmitDeclarations,
  88. declarationMap: shouldEmitDeclarations
  89. },
  90. exclude: ['**/__tests__', 'test-dts']
  91. }
  92. })
  93. // we only need to check TS and generate declarations once for each build.
  94. // it also seems to run into weird issues when checking multiple times
  95. // during a single build.
  96. hasTSChecked = true
  97. const entryFile =
  98. format === 'esm-bundler-runtime' ? `src/runtime.ts` : `src/index.ts`
  99. return {
  100. input: resolve(entryFile),
  101. // Global and Browser ESM builds inlines everything so that they can be
  102. // used alone.
  103. external:
  104. isGlobalBuild || isRawESMBuild
  105. ? []
  106. : knownExternals.concat(Object.keys(pkg.dependencies || [])),
  107. plugins: [
  108. json({
  109. namedExports: false
  110. }),
  111. tsPlugin,
  112. createReplacePlugin(
  113. isProductionBuild,
  114. isBundlerESMBuild,
  115. (isGlobalBuild || isRawESMBuild || isBundlerESMBuild) &&
  116. !packageOptions.enableNonBrowserBranches,
  117. isRuntimeCompileBuild
  118. ),
  119. ...plugins
  120. ],
  121. output,
  122. onwarn: (msg, warn) => {
  123. if (!/Circular/.test(msg)) {
  124. warn(msg)
  125. }
  126. }
  127. }
  128. }
  129. function createReplacePlugin(
  130. isProduction,
  131. isBundlerESMBuild,
  132. isBrowserBuild,
  133. isRuntimeCompileBuild
  134. ) {
  135. const replacements = {
  136. __COMMIT__: `"${process.env.COMMIT}"`,
  137. __VERSION__: `"${masterVersion}"`,
  138. __DEV__: isBundlerESMBuild
  139. ? // preserve to be handled by bundlers
  140. `(process.env.NODE_ENV !== 'production')`
  141. : // hard coded dev/prod builds
  142. !isProduction,
  143. // this is only used during tests
  144. __TEST__: isBundlerESMBuild ? `(process.env.NODE_ENV === 'test')` : false,
  145. // If the build is expected to run directly in the browser (global / esm builds)
  146. __BROWSER__: isBrowserBuild,
  147. // is targeting bundlers?
  148. __BUNDLER__: isBundlerESMBuild,
  149. // support compile in browser?
  150. __RUNTIME_COMPILE__: isRuntimeCompileBuild,
  151. // support options?
  152. // the lean build drops options related code with buildOptions.lean: true
  153. __FEATURE_OPTIONS__: !packageOptions.lean && !process.env.LEAN,
  154. __FEATURE_SUSPENSE__: true
  155. }
  156. // allow inline overrides like
  157. //__RUNTIME_COMPILE__=true yarn build runtime-core
  158. Object.keys(replacements).forEach(key => {
  159. if (key in process.env) {
  160. replacements[key] = process.env[key]
  161. }
  162. })
  163. return replace(replacements)
  164. }
  165. function createProductionConfig(format) {
  166. return createConfig(format, {
  167. file: resolve(`dist/${name}.${format}.prod.js`),
  168. format: outputConfigs[format].format
  169. })
  170. }
  171. function createMinifiedConfig(format) {
  172. const { terser } = require('rollup-plugin-terser')
  173. return createConfig(
  174. format,
  175. {
  176. file: resolve(`dist/${name}.${format}.prod.js`),
  177. format: outputConfigs[format].format
  178. },
  179. [
  180. terser({
  181. module: /^esm/.test(format)
  182. })
  183. ]
  184. )
  185. }

还有就是 tsconfig.json,需要吧sourceMap改成true,这里也直接上代码了

  1. {
  2. "compilerOptions": {
  3. "baseUrl": ".",
  4. "outDir": "dist",
  5. "sourceMap": true,
  6. "target": "esnext",
  7. "module": "esnext",
  8. "moduleResolution": "node",
  9. "allowJs": false,
  10. "noUnusedLocals": true,
  11. "strictNullChecks": true,
  12. "noImplicitAny": true,
  13. "noImplicitThis": true,
  14. "experimentalDecorators": true,
  15. "resolveJsonModule": true,
  16. "esModuleInterop": true,
  17. "removeComments": false,
  18. "jsx": "preserve",
  19. "lib": ["esnext", "dom"],
  20. "types": ["jest", "puppeteer", "node"],
  21. "rootDir": ".",
  22. "paths": {
  23. "@vue/*": ["packages/*/src"]
  24. }
  25. },
  26. "include": [
  27. "packages/global.d.ts",
  28. "packages/runtime-dom/jsx.d.ts",
  29. "packages/*/src",
  30. "packages/*/__tests__",
  31. "test-dts"
  32. ]
  33. }

然后随便建一个目录,我这里叫public, 像这样

然后执行 yarn dev 或者 cnpm run dev

然后在html启动引入vue.js

path : ../packages/vue/dist/vue.global.js

上文提到 vuebeforeCreatecreated 都换成了 setup,那咱们就来试一下,当然了,本篇的例子直接食用cdn方式来实验的,毕竟现在vue也没正式发版,很多东西都不确定,配那么全也没有必要

稍微来搞一个模版,直接上代码了

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. var App = {
  14. template: `
  15. <div class="container">
  16. </div>`,
  17. setup() {
  18. }
  19. }
  20. Vue.createApp().mount(App, '#app')
  21. </script>
  22. </body>
  23. </html>

大概就是这种感觉,上文说过,vue现在折了一个中,所以vue3里推出了两个新东西(其实是一个,另一个是变种)

reactive - 监听数据

咱们来直接上例子 正常什么都不用的情况下,如果要是想用数据,就直接写在setup函数返回的json里就可以,像这样

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{aaa}}
  18. </div>`,
  19. setup() {
  20. return {
  21. aaa: 123
  22. }
  23. }
  24. }
  25. Vue.createApp().mount(App, '#app')
  26. </script>
  27. </body>
  28. </html>

但是咱们现在这个数据,其实不是受监听的,大家可以试一下单独搞个json,然后改变值页面是不会渲染的,这时候,就能用到咱们的reactive

用法也很简单,直接调用函数里面的参数就是你的数据,返回值就是一个可监听的数据

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. 名字 {{data.name}} <br/>
  18. 年龄 {{data.age}} <br/>
  19. </div>`,
  20. setup() {
  21. let data = reactive({name: 'name', age: 18})
  22. return {
  23. data
  24. }
  25. }
  26. }
  27. Vue.createApp().mount(App, '#app')
  28. </script>
  29. </body>
  30. </html>

这就可以了

当然了,可能会有人感觉麻烦,感觉所有数据都得手动来监听一下,其实如果你先麻烦的化可以直接在setup return 出来的json外直接包一个reactive,这样你就会到了vue2的写法,像这样

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. 名字 {{data.name}} <br/>
  18. 年龄 {{data.age}} <br/>
  19. </div>`,
  20. setup() {
  21. let data = {name: 'name', age: 18}
  22. return reactive({
  23. data
  24. })
  25. }
  26. }
  27. Vue.createApp().mount(App, '#app')
  28. </script>
  29. </body>
  30. </html>

而且,reactive 自带深度监听,也就是说你这个数据不管套了多少层,json里有多少数组,都会监听的

ref

这个是reactive 的一个小兄弟,可能这时候有人会感觉奇怪,ref不是获取dom元素用的么,跟reactive咋还能有关系呢,这个其实官方也没定下来,没确定下来到底是获取元素还是包装数据,这个可能得需要vue发布了之后才能知道

先不说这个,先来说说ref怎么用,首先vue3ref也能装数据,只不过它是通过reactive来实现的,背后也还是reactive

其实大家看个例子一眼就能看明白

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{refData}} <button @click="handleClick()">按钮</button>
  18. </div>`,
  19. setup() {
  20. let refData = ref(0);
  21. const handleClick = () =>{
  22. refData.value+=1
  23. }
  24. console.log(refData)
  25. return {
  26. refData,
  27. handleClick
  28. }
  29. }
  30. }
  31. Vue.createApp().mount(App, '#app')
  32. </script>
  33. </body>
  34. </html>

可以看到我在修改的时候改的是refData.value,没错,其实

let refData = ref(0); == let refData = reactive({value: 0 })

但是在模板里用的时候就不用.value,毕竟vue 自己家的东西,至于vue为什么要这么做,就要看最终发布是什么样子了

肯定有人会想,那我如果就是想得到ref,比如一个input,该怎么办呢?很简单,这里也直接上代码,然后再解释

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, watch, onMounted } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. <input ref="input1" value="321" />
  18. </div>`,
  19. setup() {
  20. const input1 = ref(null);
  21. console.log(input1.value)
  22. onMounted(()=>{
  23. console.log(input1.value.value)
  24. })
  25. return {
  26. input1
  27. }
  28. }
  29. }
  30. Vue.createApp().mount(App, '#app')
  31. </script>
  32. </body>
  33. </html>

这里先是给这个ref一个初始的一个null,然后返回出去给到咱们的元素,这也是咱们在setup里直接拿不到这个value的原因,因为它还没完事,什么时候能拿到,onMounted的时候,这个是生命周期相信大家都认识,生命周期在下文会说,这里不多赘述

react熟悉的兄弟这时候很有感觉对吧?

Props

其实这个props没有太多的变化,几句话就能说清,参数约定也还在,像这样

稍微有点区别的就是之前咱们用props数据的时候,可以直接this.props.xxx这样获取,现在他是直接放到setup的参数里了,像这样

当然看到这大家应该能感觉到和什么比较像了哈?

当然了,毕竟vue3是主推ts版的,所以当然ts这一套也全都可以用,比如这样

当然,html里肯定是不能直接写ts的,这里就是说明意思

computed

这个computed有两种写法,一种是直接传一个函数,这个其实就相当于过去computed只有get,所以当你设置值的时候,是会报错的

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, computed } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{count}} <button @click="handleClick()">按钮</button>
  18. </div>`,
  19. setup() {
  20. let refData = ref(0);
  21. let count = computed(()=>{
  22. return refData.value;
  23. })
  24. const handleClick = () =>{
  25. count.value+=1
  26. }
  27. console.log(refData)
  28. return {
  29. count,
  30. handleClick
  31. }
  32. }
  33. }
  34. Vue.createApp().mount(App, '#app')
  35. </script>
  36. </body>
  37. </html>

当然了,想和过去完全一样的写法当然也可以,也就是第二中写法,直接传一个json

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, computed } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{count}} <button @click="handleClick()">按钮</button>
  18. </div>`,
  19. setup() {
  20. let refData = ref(0);
  21. let count = computed({
  22. get(){
  23. return refData.value;
  24. },
  25. set(value){
  26. console.log(value)
  27. refData.value = value;
  28. }
  29. })
  30. const handleClick = () =>{
  31. count.value+=1
  32. }
  33. console.log(refData)
  34. return {
  35. count,
  36. handleClick
  37. }
  38. }
  39. }
  40. Vue.createApp().mount(App, '#app')
  41. </script>
  42. </body>
  43. </html>

这基本上和2.x没什么区别了,就不多解释了

不过稍微注意一点,这个computed自身不带任何渲染,也就是说,如果这个refData 不是一个ref或者说是reactive的话,如果修改了数据,是会变没错,但是整个vue不会去重新渲染,这也是为了灵活,如果这些东西都带的话,也就没有办法去增强性能了,所以reactive才是vue3整个的核心

readonly

这个用法其实跟上述的都差不多,调一下函数返回一个值,类似这样

let Version = readonly(1.1.3)

这时候可能有人会觉得有些奇怪,为什么会有这个东西,直接用const或者tsreadonly不就好了么,当然了,这肯定是没错,用const声明在这个函数里改确实会报错,但是别忘了,这个值咱们是要在setup里返回出去的,或者传给别的地方,也就是通过vue中转了一下,这时候就不行了,这也是有用的

watch

注意,这个watch必须监听的是一个reactive的数据,如果是一个普通数据的话,用这个watch是监听不出来的,然后咱们就来试试

还用刚才的例子来改一下

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, watch } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{refData}} <button @click="handleClick()">按钮</button>
  18. </div>`,
  19. setup() {
  20. let refData = ref(0);
  21. const handleClick = () =>{
  22. refData.value+=1
  23. }
  24. watch(refData,(val, oldVal)=>{
  25. console.log(val, oldVal)
  26. })
  27. return {
  28. refData,
  29. handleClick
  30. }
  31. }
  32. }
  33. Vue.createApp().mount(App, '#app')
  34. </script>
  35. </body>
  36. </html>

也是跟过去的时候差不多,不过稍微有点区别的是,咱们有的时候可能是某一个特殊的时间要监听,过了这个特殊的时候就不监听了,这个watch会返回一个函数,执行的话就会直接停止监听

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, watch } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{refData}} <button @click="handleClick()">按钮</button>
  18. <button @click="handleStop">停止</button>
  19. </div>`,
  20. setup() {
  21. let refData = ref(0);
  22. const handleClick = () =>{
  23. refData.value+=1
  24. }
  25. let stop = watch(refData,(val, oldVal)=>{
  26. console.log(val, oldVal)
  27. })
  28. const handleStop = () =>{
  29. stop()
  30. }
  31. return {
  32. refData,
  33. handleClick,
  34. handleStop
  35. }
  36. }
  37. }
  38. Vue.createApp().mount(App, '#app')
  39. </script>
  40. </body>
  41. </html>

还蛮方便的对吧~

生命周期

其实我相信生命周期这东西大家还是比较关心的,比如mounted直接写在外面就可以了,当然了,相信看到这大家应该明白vue3的写项目的模式了,没错,也是一个函数,在setup里调用 直接上代码了

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <script src="../packages/vue/dist/vue.global.js"></script>
  11. <div id="app"></div>
  12. <script>
  13. const { reactive, ref, watch, onMounted } = Vue
  14. var App = {
  15. template: `
  16. <div class="container">
  17. {{refData}} <button @click="handleClick()">按钮</button>
  18. <button @click="handleStop">停止</button>
  19. </div>`,
  20. setup() {
  21. onMounted(()=>{
  22. console.log('mounted~');
  23. })
  24. let refData = ref(0);
  25. const handleClick = () =>{
  26. refData.value+=1
  27. }
  28. let stop = watch(refData,(val, oldVal)=>{
  29. console.log(val, oldVal)
  30. })
  31. const handleStop = () =>{
  32. stop()
  33. }
  34. return {
  35. refData,
  36. handleClick,
  37. handleStop
  38. }
  39. }
  40. }
  41. Vue.createApp().mount(App, '#app')
  42. </script>
  43. </body>
  44. </html>

还是比较简单的,当然了,有onMounted就有on别的,这都一样的,除了那个卸载的改名了,叫onUnmounted

跨组件共享数据

vue2.x里咱们组件间共享数据是不是只能props,稍微复杂点的共享就得用vuex,但是这个东西一多,就太乱了

所以vue3里提供了另外两个东西

provide - 提供数据

inject - 得到数据

这里我就直接写伪代码了

  1. const userData = Symbol();
  2. const Cmp1 = {
  3. setup(){
  4. let user = reactive({name: "aaa", age: 18});
  5. provide(userData, user);
  6. }
  7. }
  8. const Cmp2 = {
  9. setup(){
  10. const user = inject(userData, {});
  11. }
  12. }

这个就很简单了,大家应该能看明白,不过为什么要用Symbol呢,大家知道Symbol出来的东西永远都是唯一的,这也是官方推荐的写法,如果要是你纯自定义,很容易重名或者打错字母什么之类的,所以咱们只要保管好这个key,数据共享就会变的很方便

这个inject第二个参数就是默认值,也就是说没有这个数据的时候默认值是什么,这个还是比较好理解的

这个多说一句,其实我个人是比较喜欢的,其实大家可以仔细想想,之前在写vue的时候,很多程度的时间都在搞父子组件,什么兄弟组件,这个那个的互相传,很费劲,有了这个,就可以完全抛弃之前的写法了,只需要想办法怎么把这个数据统一管理、声明也好,省了很多麻烦事

虽说2.x里也有,不过官方也说了,推荐在高级组件里或者库里用,也不推荐你写,毕竟还有很多没完善

总结

其实看到这大家应该都感觉到了,vue3这回还是非常注重这个setup函数的,其实这样也有很大的好处,等于说vue这次把很多的决定权交给你了,不再是所有的东西都是它背后做的了,也很方便

当然了,可能会有人觉得所有的东西全放到一个地方太乱了,其实也还好,因为你可以自己去做模块化什么之类的,这个看你自己的管理了,不过我预感可能官方还会出一个什么之类的规范

不过现在的小道消息也不可信,具体都还需要等具体vue3发版才能知道。

可能有朋友比较喜欢vue 2.x的装饰器写法,这里官方说明了,装饰器现在毕竟还不稳定,框架上也有一下烂七八糟的不方便,this什么的,对继承也没什么帮助,所以vue3就直接舍弃class写法了,不过。。。暂时吧,具体怎么样还是得看上线了之后。

- END -

推荐阅读:

1GitHub 上能挖矿的神仙技巧 - 如何发现优秀开源项目

2. 56 道高频 JavaScript 与 ES6+ 的面试题及答案

3. 解密 BAT 大厂的初、中、高级程序员的进化之路(前端)

4. 重磅:硬核前端面试开源项目汇总(进大厂必备)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/795092
推荐阅读
相关标签
  

闽ICP备14008679号