{"id":15307963,"url":"https://github.com/wscats/vue-cli","last_synced_at":"2025-04-06T00:08:38.342Z","repository":{"id":40625802,"uuid":"84533659","full_name":"Wscats/vue-cli","owner":"Wscats","description":"📃基于 Vue3.0 Composition Api 快速构建实战项目","archived":false,"fork":false,"pushed_at":"2019-12-26T01:32:34.000Z","size":16602,"stargazers_count":484,"open_issues_count":1,"forks_count":123,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-03-29T23:09:05.244Z","etag":null,"topics":["axios","cnode","hooks","vue","vue-composition-api","vue-function-api","vue3","vuex","webpack"],"latest_commit_sha":null,"homepage":"https://wscats.github.io/vue-cli/dist/index.html","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Wscats.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-10T07:47:44.000Z","updated_at":"2025-03-18T06:06:03.000Z","dependencies_parsed_at":"2022-09-01T10:10:47.334Z","dependency_job_id":null,"html_url":"https://github.com/Wscats/vue-cli","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fvue-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fvue-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fvue-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Wscats%2Fvue-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Wscats","download_url":"https://codeload.github.com/Wscats/vue-cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247415967,"owners_count":20935387,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axios","cnode","hooks","vue","vue-composition-api","vue-function-api","vue3","vuex","webpack"],"created_at":"2024-10-01T08:13:08.029Z","updated_at":"2025-04-06T00:08:38.317Z","avatar_url":"https://github.com/Wscats.png","language":"Vue","readme":"# Quick Start\n\n- 项目源码: [https://github.com/Wscats/vue-cli](https://github.com/Wscats/vue-cli)\n\n本项目综合运用了 `Vue3.0` 的新特性，适合新手学习😁\n\n- 基于 `Composition API` 即 `Function-based API` 进行改造，配合 `Vue Cli`，优先体验 `Vue3` 特性\n- 使用单例对象模式进行组件通信\n- 使用 `axios` 库进行网络请求，`weui` 库实现 UI 界面\n\n```bash\n# 安装依赖\nnpm install\n# 在浏览器打开localhost:8080查看页面，并实时热更新\nnpm run serve\n# 发布项目\nnpm run build\n```\n\n建议配合 Visual Studio Code 和 [Vue 3 Snippets](https://github.com/Wscats/vue-snippets) 代码插件食用Ψ(￣∀￣)Ψ。\n\n# Dependencies\n\n以下是项目运用到的依赖，`@vue/composition-api` 配合 `vue` 模块让我们 `Vue2.0` 版本可以抢先体验 `Vue3.0` 的新特性，`axios` 是辅助我们发送网络请求得到数据的工具库，`weui`是一套与微信原生视觉一致的基础样式库，方便我们快速搭建项目页面。\n\n```js\n\"@vue/composition-api\": \"^0.3.4\",\n\"axios\": \"^0.19.0\",\n\"core-js\": \"^3.4.3\",\n\"vue\": \"^2.6.10\",\n\"weui\": \"^2.1.3\"\n```\n\n# Directory Structure\n\n```js\n├── src\n│   ├── App.vue                          # 组件入口\n│   ├── assets                           # 资源目录\n│   ├── stores/index.js                  # 状态管理\n│   ├── components                       # 组件目录\n│   │   ├── Header.vue                   # 头部组件\n│   │   ├── Search.vue                   # 搜索框组件\n│   │   ├── Panel.vue                    # 列表组件\n│   ├── main.js                          # 项目入口\n├── public                               # 模板文件\n├── vue.config.js                        # 脚手架配置文件\n├── screenshot                           # 程序截图\n```\n\n\n# Composition API\n\n```bash\nnpm install @vue/composition-api --save\n```\n\n使用 `npm` 命令下载了 `@vue/composition-api` 插件以后，引入该模块后，需要显式调用 `Vue.use(VueCompositionApi)` ，按照文档在 `main.js` 引用便开启了 `Composition API` 的能力。\n```js\n// main.js\nimport Vue from 'vue'\nimport App from './App.vue'\n// 1.引入Composition API模块\nimport VueCompositionApi from '@vue/composition-api'\n\nVue.config.productionTip = false\n// 2.不要漏了显式调用 VueCompositionApi\nVue.use(VueCompositionApi)\n\nnew Vue({\n  render: h =\u003e h(App),\n}).$mount('#app')\n```\n\n\n```bash\nnpm install weui --save\n```\n\n我们同样使用 `npm` 安装 `weui` 模块，然后在 `main.js` 中引入 `weui`的基础样式库，方便我们可以在全局使用微信基础样式构建项目页面。\n\n```js\n// main.js\nimport Vue from 'vue'\nimport App from './App.vue'\n// 全局引入 `weui` 的基础样式库\nimport 'weui'\nimport VueCompositionApi from '@vue/composition-api'\n\nVue.config.productionTip = false\nVue.use(VueCompositionApi)\n\nnew Vue({\n  render: h =\u003e h(App),\n}).$mount('#app')\n```\n\n回到 `App.vue`，保留 `components` 属性值清空 `\u003ctemplate\u003e` 模板的内容，删除 `\u003cstyle\u003e` 模板，等待重新引入新的组件。\n\n```html\n\u003ctemplate\u003e\n  \u003cdiv id=\"app\"\u003e\n    Hello World\n  \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nexport default {\n  name: \"app\",\n  components: {}\n};\n\u003c/script\u003e\n```\n\n在 `src/components` 目录下新建第一个组件，取名为 `Header.vue` 写入以下代码，[点击查看源代码](https://github.com/Wscats/vue-cli/blob/master/src/components/Header.vue)：\n\n```html\n\u003ctemplate\u003e\n  \u003cheader :style=\"{\n    backgroundColor: color?color:defaultColor\n  }\"\u003e{{title}}\u003c/header\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nimport { reactive } from \"@vue/composition-api\";\nexport default {\n  // 父组件传递进来更改该头部组件的属性值\n  props: {\n    // 标题\n    title: String,\n    // 颜色\n    color: String\n  },\n  setup() {\n    const state = reactive({\n      defaultColor: \"red\"\n    });\n    return {\n      ...state\n    };\n  }\n};\n\u003c/script\u003e\n\u003cstyle scoped\u003e\nheader {\n  height: 50px;\n  width: 100%;\n  line-height: 50px;\n  text-align: center;\n  color: white;\n}\n\u003c/style\u003e\n```\n\n---\n\n# setup\n\n这里运用了一个全新的属性 `setup` ，这是一个组件的入口，让我们可以运用 `Vue3.0` 暴露的新接口，它运行在组件被实例化时候，`props` 属性被定义之后，实际上等价于 `Vue2.0` 版本的 `beforeCreate` 和 `Created` 这两个生命周期，`setup` 返回的是一个对象，里面的所有被返回的属性值，都会被合并到 `Vue2.0` 的 `render` 渲染函数里面，在单文件组件中，它将配合 `\u003ctemplate\u003e` 模板的内容，完成 `Model` 到 `View` 之间的绑定，在未来版本中应该还会支持返回 `JSX` 代码片段。\n\n```html\n\u003ctemplate\u003e\n  \u003c!-- View --\u003e\n  \u003cdiv\u003e{{name}}\u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nimport { reactive } from '@vue/composition-api'\nexport default {\n  setup() {\n    const state = reactive({ name: 'Eno Yao' });\n    // return 暴露到 template 中\n    return {\n      // Model\n      ...state\n    }\n  }\n}\n\u003c/script\u003e\n```\n\n# reactive\n\n在 `setup` 函数里面， 我们适应了 Vue3.0 的第一个新接口 `reactive` 它主要是处理你的对象让它经过 `Proxy` 的加工变为一个响应式的对象，类似于 `Vue2.0` 版本的 `data` 属性，需要注意的是加工后的对象跟原对象是不相等的，并且加工后的对象属于深度克隆的对象。\n\n```js\nconst state = reactive({ name: 'Eno Yao' })\n```\n\n# props\n\n在 `Vue2.0` 中我们可以使用 `props` 属性值完成父子通信，在这里我们需要定义 `props` 属性去定义接受值的类型，然后我们可以利用 `setup` 的第一个参数获取 `props` 使用。\n\n```js\nexport default {\n  props: {\n    // 标题\n    title: String,\n    // 颜色\n    color: String\n  },\n  setup(props) {\n    // 这里可以使用父组件传过来的 props 属性值\n  }\n};\n```\n\n我们在 `App.vue` 里面就可以使用该头部组件，有了上面的 `props` 我们可以根据传进来的值，让这个头部组件呈现不同的状态。\n\n```html\n\u003ctemplate\u003e\n  \u003cdiv id=\"app\"\u003e\n    \u003c!-- 复用组件，并传入 props 值，让组件呈现对应的状态 --\u003e\n    \u003cHeader title=\"Eno\" color=\"red\" /\u003e\n    \u003cHeader title=\"Yao\" color=\"blue\" /\u003e\n    \u003cHeader title=\"Wscats\" color=\"yellow\" /\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nimport Header from \"./components/Header.vue\";\nexport default {\n  name: \"app\",\n  components: {\n    Header,\n  }\n};\n\u003c/script\u003e\n```\n\n\u003cimg src=\"./screenshot/1.gif\" /\u003e\n\n# context\n\n`setup` 函数的第二个参数是一个上下文对象，这个上下文对象中包含了一些有用的属性，这些属性在 `Vue2.0` 中需要通过 `this` 才能访问到，在 `vue3.0` 中，访问他们变成以下形式：\n\n```js\nsetup(props, ctx) {\n  console.log(ctx) // 在 setup() 函数中无法访问到 this\n  console.log(this) // undefined\n}\n```\n\n具体能访问到以下有用的属性：\n\n- root\n- parent\n- refs\n- attrs\n- listeners\n- isServer\n- ssrContext\n- emit\n\n---\n\n完成上面的 `Header.vue` 我们就编写 `Search.vue` 搜索框组件，继续再 `src/components` 文件夹下面新建 `Search.vue` 文件，[点击查看源代码](https://github.com/Wscats/vue-cli/blob/master/src/components/Search.vue)。\n\n```html\n\u003ctemplate\u003e\n  \u003cdiv :class=\"['weui-search-bar', {'weui-search-bar_focusing' : isFocus}]\" id=\"searchBar\"\u003e\n    \u003cform class=\"weui-search-bar__form\"\u003e\n      \u003cdiv class=\"weui-search-bar__box\"\u003e\n        \u003ci class=\"weui-icon-search\"\u003e\u003c/i\u003e\n        \u003cinput\n          v-model=\"searchValue\"\n          ref=\"inputElement\"\n          type=\"search\"\n          class=\"weui-search-bar__input\"\n          id=\"searchInput\"\n          placeholder=\"搜索\"\n          required\n        /\u003e\n        \u003ca href=\"javascript:\" class=\"weui-icon-clear\" id=\"searchClear\"\u003e\u003c/a\u003e\n      \u003c/div\u003e\n      \u003clabel @click=\"toggle\" class=\"weui-search-bar__label\" id=\"searchText\"\u003e\n        \u003ci class=\"weui-icon-search\"\u003e\u003c/i\u003e\n        \u003cspan\u003e搜索\u003c/span\u003e\n      \u003c/label\u003e\n    \u003c/form\u003e\n    \u003ca @click=\"toggle\" href=\"javascript:\" class=\"weui-search-bar__cancel-btn\" id=\"searchCancel\"\u003e取消\u003c/a\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nimport { reactive, toRefs, watch } from \"@vue/composition-api\";\nimport store from \"../stores\";\nexport default {\n  // setup相当于2.x版本的beforeCreate生命周期\n  setup() {\n    // reactive() 函数接收一个普通对象，返回一个响应式的数据对象\n    const state = reactive({\n      searchValue: \"\",\n      // 搜索框两个状态，聚焦和非聚焦\n      isFocus: false,\n      inputElement: null\n    });\n    // 切换搜索框状态的方法\n    const toggle = () =\u003e {\n      // 让点击搜索后出现的输入框自动聚焦\n      state.inputElement.focus();\n      state.isFocus = !state.isFocus;\n    };\n    // 监听搜索框的值\n    watch(\n      () =\u003e {\n        return state.searchValue;\n      },\n      () =\u003e {\n        // 存储输入框到状态 store 中心，用于组件通信\n        store.setSearchValue(state.searchValue);\n        // window.console.log(state.searchValue);\n      }\n    );\n    return {\n      // 将 state 上的每个属性，都转化为 ref 形式的响应式数据\n      ...toRefs(state),\n      toggle\n    };\n  }\n};\n\u003c/script\u003e\n```\n\n# toRefs\n\n可以看到我们上面用了很多的新属性，我们先介绍 `toRefs` ，函数可以将 `reactive()` 创建出来的响应式对象，转换为普通的对象，只不过，这个对象上的每个属性节点，都是 `ref()` 类型的响应式数据，配合 `v-model` 指令能完成数据的双向绑定，在开发中非常高效。\n\n```js\nimport { reactive, toRefs } from \"@vue/composition-api\";\nexport default {\n  setup() {\n    const state = reactive({ name: 'Eno Yao' })\n  }\n  return {\n    // 直接返回 state 那么数据会是非响应式的， MV 单向绑定\n    // ...state,\n    // toRefs 包装后返回 state 那么数据会是响应式的， MVVM 双向绑定\n    ...toRefs(state),\n  };\n}\n```\n\n\u003cimg src=\"./screenshot/2.gif\" /\u003e\n\n# template refs\n\n这里的输入框拥有两个状态，一个是有输入框的状态和无输入框的状态，所以我们需要一个布尔值 `isFocus` 来控制状态，封装了一个 `toggle` 方法，让 `isFocus` 值切换真和假两个状态。\n\n```js\nconst toggle = () =\u003e {\n  // isFocus 值取反\n  state.isFocus = !state.isFocus;\n};\n```\n\n然后配合 `v-bind:class` 指令，让 `weui-search-bar_focusing` 类名根据 `isFocus` 值决定是否出现，从而更改搜索框的状态。\n\n```html\n\u003cdiv :class=\"['weui-search-bar', {'weui-search-bar_focusing' : isFocus}]\" id=\"searchBar\"\u003e\n```\n\n这里的搜索输入框放入了 `v-model` 指令，用于接收用户的输入信息，方便后面配合列表组件执行检索逻辑，还放入了 `ref` 属性，用于获取该 `\u003cinput/\u003e` 标签的元素节点，配合`state.inputElement.focus()` 原生方法，在切换搜索框状态的时候光标自动聚焦到输入框，增强用户体验。\n\n```html\n\u003cinput\n  v-model=\"searchValue\"\n  ref=\"inputElement\"\n/\u003e\n```\n\n\u003cimg src=\"./screenshot/3.gif\" /\u003e\n\n# watch\n\n`watch()` 函数用来监视某些数据项的变化，从而触发某些特定的操作，使用之前还是需要按需导入，监听 `searchValue` 的变化，然后触发回调函数里面的逻辑，也就是监听用户输入的检索值，然后触发回调函数的逻辑把 `searchValue` 值存进我们创建 `store` 对象里面，方面后面和 `Panel.vue` 列表组件进行数据通信：\n\n```js\nimport { reactive, watch } from \"@vue/composition-api\";\nimport store from \"../stores\";\nexport default {\n  setup() {\n    const state = reactive({\n      searchValue: \"\",\n    });\n    // 监听搜索框的值\n    watch(\n      () =\u003e {\n        return state.searchValue;\n      },\n      () =\u003e {\n        // 存储输入框到状态 store 中心，用于组件通信\n        store.setSearchValue(state.searchValue);\n      }\n    );\n    return {\n      ...toRefs(state)\n    };\n  }\n};\n```\n\n# state management\n\n在这里我们维护一份数据来实现共享状态管理，也就是说我们新建一个 `store.js` 暴露出一个 `store` 对象共享 `Panel` 和 `Search` 组件的 `searchValue` 值，当 `Search.vue` 组件从输入框接受到 `searchValue` 检索值，就放到 `store.js` 的 `store` 对象中，然后把该对象注入到 `Search` 组件中，那么两个组件都可以共享 `store` 对象中的值，为了方便调试我们还分别封装了 `setSearchValue` 和 `getSearchValue` 来去操作该 `store` 对象，这样我们就可以跟踪状态的改变。\n\n```js\n// store.js\nexport default {\n    state: {\n        searchValue: \"\"\n    },\n    // 设置搜索框的值\n    setSearchValue(value) {\n        this.state.searchValue = value\n    },\n    // 获取搜索框的值\n    getSearchValue() {\n        return this.state.searchValue\n    }\n}\n```\n\n---\n\n完成上面的 `Search.vue` 我们紧接着编写 `Panel.vue` 搜索框组件，继续再 `src/components` 文件夹下面新建 `Panel.vue` 文件，[点击查看源代码](https://github.com/Wscats/vue-cli/blob/master/src/components/Panel.vue)。\n\n```html\n\u003ctemplate\u003e\n  \u003cdiv class=\"weui-panel weui-panel_access\"\u003e\n    \u003cdiv v-for=\"(n,index) in newComputed\" :key=\"index\" class=\"weui-panel__bd\"\u003e\n      \u003ca href=\"javascript:void(0);\" class=\"weui-media-box weui-media-box_appmsg\"\u003e\n        \u003cdiv class=\"weui-media-box__hd\"\u003e\n          \u003cimg class=\"weui-media-box__thumb\" :src=\"n.author.avatar_url\" alt /\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"weui-media-box__bd\"\u003e\n          \u003ch4 class=\"weui-media-box__title\" v-text=\"n.title\"\u003e\u003c/h4\u003e\n          \u003cp class=\"weui-media-box__desc\" v-text=\"n.author.loginname\"\u003e\u003c/p\u003e\n        \u003c/div\u003e\n      \u003c/a\u003e\n    \u003c/div\u003e\n    \u003cdiv @click=\"loadMore\" class=\"weui-panel__ft\"\u003e\n      \u003ca href=\"javascript:void(0);\" class=\"weui-cell weui-cell_access weui-cell_link\"\u003e\n        \u003cdiv class=\"weui-cell__bd\"\u003e查看更多\u003c/div\u003e\n        \u003cspan class=\"weui-cell__ft\"\u003e\u003c/span\u003e\n      \u003c/a\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\u003cscript\u003e\nimport { reactive, toRefs, onMounted, computed } from \"@vue/composition-api\";\nimport axios from \"axios\";\nimport store from \"../stores\";\nexport default {\n  setup() {\n    const state = reactive({\n      // 页数\n      page: 1,\n      // 列表数据\n      news: [],\n      // 通过搜索框的值去筛选劣列表数据\n      newComputed: computed(() =\u003e {\n        // 判断是否输入框是否输入了筛选条件，如果没有返回原始的 news 数组\n        if (store.state.searchValue) {\n          return state.news.filter(item =\u003e {\n            if (item.title.indexOf(store.state.searchValue) \u003e= 0) {\n              return item;\n            }\n          });\n        } else {\n          return state.news;\n        }\n      }),\n      searchValue: store.state\n    });\n    // 发送 ajax 请求获取列表数据\n    const loadMore = async () =\u003e {\n      // 获取列表数据\n      let data = await axios.get(\"https://cnodejs.org/api/v1/topics\", {\n        params: {\n          // 每一页的主题数量\n          limit: 10,\n          // 页数\n          page: state.page\n        }\n      });\n      // 叠加页数\n      state.page += 1;\n      state.news = [...state.news, ...data.data.data];\n    };\n    onMounted(() =\u003e {\n      // 首屏加载的时候触发请求\n      loadMore();\n    });\n    return {\n      // 让数据保持响应式\n      ...toRefs(state),\n      // 查看更多事件\n      loadMore\n    };\n  }\n};\n\u003c/script\u003e\n```\n\n# lifecycle hooks\n\n`Vue3.0` 的生命周期钩子和之前不一样，新版本都是以 `onXxx()` 函数注册使用，同样需要局部引入生命周期的对应模块：\n\n```js\nimport { onMounted, onUpdated, onUnmounted } from \"@vue/composition-api\";\nexport default {\n  setup() {\n    const loadMore = () =\u003e {};\n    onMounted(() =\u003e {\n      loadMore();\n    });\n    onUpdated(() =\u003e {\n      console.log('updated!')\n    })\n    onUnmounted(() =\u003e {\n      console.log('unmounted!')\n    })\n    return {\n      loadMore\n    };\n  }\n};\n```\n\n以下是新旧版本生命周期的对比：\n\n- \u003cs\u003e`beforeCreate`\u003c/s\u003e -\u003e use `setup()`\n- \u003cs\u003e`created`\u003c/s\u003e -\u003e use `setup()`\n- `beforeMount` -\u003e `onBeforeMount`\n- `mounted` -\u003e `onMounted`\n- `beforeUpdate` -\u003e `onBeforeUpdate`\n- `updated` -\u003e `onUpdated`\n- `beforeDestroy` -\u003e `onBeforeUnmount`\n- `destroyed` -\u003e `onUnmounted`\n- `errorCaptured` -\u003e `onErrorCaptured`\n\n同时新版本还提供了两个全新的生命周期帮助我们去调试代码：\n\n- onRenderTracked\n- onRenderTriggered\n\n在 `Panel` 列表组件中，我们注册 `onMounted` 生命周期，并在里面触发请求方法 `loadMore` 以便从后端获取数据到数据层，这里我们使用的是 `axios` 网络请求库，所以我们需要安装该模块：\n\n```bash\nnpm install axios --save\n```\n\n封装了一个请求列表数据方法，接口指向的是 `Cnode` 官网提供的 `API` ，由于 `axios` 返回的是 `Promise` ，所以配合 `async` 和 `await` 可以完美的编写异步逻辑，然后结合`onMounted` 生命周期触发，并将方法绑定到视图层的查看更多按钮上，就可以完成列表首次的加载和点击查看更多的懒加载功能。\n\n```js\n// 发送 ajax 请求获取列表数据\nconst loadMore = async () =\u003e {\n  // 获取列表数据\n  let data = await axios.get(\"https://cnodejs.org/api/v1/topics\", {\n    params: {\n      // 每一页的主题数量\n      limit: 10,\n      // 页数\n      page: state.page\n    }\n  });\n  // 叠加页数\n  state.page += 1;\n  // 合并列表数据\n  state.news = [...state.news, ...data.data.data];\n};\nonMounted(() =\u003e {\n  // 首屏加载的时候触发请求\n  loadMore();\n});\n```\n\n\u003cimg src=\"./screenshot/4.gif\" /\u003e\n\n# computed\n\n接下来我们就使用另外一个属性 `computed` 计算属性，跟 `Vue2.0` 的使用方式很相近，同样需要按需导入该模块：\n\n```js\nimport { computed } from '@vue/composition-api';\n```\n\n计算属性分两种，只读计算属性和可读可写计算属性：\n```js\n// 只读计算属性\nlet newsComputed = computed(() =\u003e news.value + 1)\n// 可读可写\nlet newsComputed = computed({\n  // 取值函数\n  get: () =\u003e news.value + 2,\n  // 赋值函数\n  set: val =\u003e {\n    news.value = news.value - 3\n  }\n})\n```\n\n\u003cimg src=\"./screenshot/5.gif\" /\u003e\n\n这里我们使用可读可写计算属性去处理列表数据，还记得我们上一个组件 `Search.vue` 吗，我们可以结合用户在搜索框输入的检索值，配合 `computed` 计算属性来筛选对我们用户有用列表数据，所以我们首先从 `store` 的共享实例里面拿到 `Search.vue` 搜索框共享的 `searchValue` ，然后利用原生字符串方法 `indexOf` 和 数组方法 `filter` 来过滤列表的数据，然后重新返回新的列表数据 `newsComputed`，并在视图层上配合 `v-for` 指令去渲染新的列表数据，这样做既可以在没搜索框检索值的时候返回原列表数据 `news` ，而在有搜索框检索值的时候返回新列表数据 `newsComputed`。\n\n```js\nimport store from \"../stores\";\nexport default {\n  setup() {\n    const state = reactive({\n      // 原列表数据\n      news: [],\n      // 通过搜索框的值去筛选后的新列表数据\n      newsComputed: computed(() =\u003e {\n        // 判断是否输入框是否输入了筛选条件，如果没有返回原始的 news 数组\n        if (store.state.searchValue) {\n          return state.news.filter(item =\u003e {\n            if (item.title.indexOf(store.state.searchValue) \u003e= 0) {\n              return item;\n            }\n          });\n        } else {\n          return state.news;\n        }\n      }),\n      searchValue: store.state\n    });\n  }\n}\n```\n\n\u003c!-- # vue-next\n\n我们也可以从 [vue-next](https://github.com/vuejs/vue-next) 这个项目中去实际感受 `Vue3.0` 的很多特性。\n\n```bash\n# 克隆该项目仓库到本地\ngit clone https://github.com/vuejs/vue-next.git\ncd vue-next\n# 安装依赖\nnpm install\n# 执行开发命令\nnpm run dev\n```\n\n如果上述命令都执行成功，将会在 `vue-next/packages/vue` 目录下会生成一个 `dist` 文件夹，里面会有一份 `vue.global.js` 最新的这份代码编译后还不到1万行，比 `Vue2.0` 还大，看了下到处的接口，已经有一定的完整性了。\n\n\u003cimg src=\"./screenshot/6.png\" /\u003e\n\n```js\nexports.BaseTransition = BaseTransition;\nexports.Comment = Comment;\nexports.Fragment = Fragment;\nexports.KeepAlive = KeepAlive;\nexports.PatchFlags = PatchFlags;\nexports.Portal = Portal;\nexports.ShapeFlags = PublicShapeFlags;\nexports.Suspense = Suspense;\nexports.Text = Text;\nexports.Transition = Transition;\nexports.TransitionGroup = TransitionGroup;\nexports.callWithAsyncErrorHandling = callWithAsyncErrorHandling;\nexports.callWithErrorHandling = callWithErrorHandling;\nexports.camelize = camelize$1;\nexports.capitalize = capitalize$1;\nexports.cloneVNode = cloneVNode;\nexports.compile = compileToFunction;\nexports.computed = computed$1;\nexports.createApp = createApp;\nexports.createBlock = createBlock;\nexports.createCommentVNode = createCommentVNode;\nexports.createHook = createHook;\nexports.createRenderer = createRenderer;\nexports.createSlots = createSlots;\nexports.createTextVNode = createTextVNode;\nexports.createVNode = createVNode;\nexports.defineComponent = defineComponent;\nexports.effect = effect;\nexports.getCurrentInstance = getCurrentInstance;\nexports.h = h;\nexports.handleError = handleError;\nexports.inject = inject;\nexports.injectHook = injectHook;\nexports.instanceWatch = instanceWatch;\nexports.isReactive = isReactive;\nexports.isReadonly = isReadonly;\nexports.isRef = isRef;\nexports.markNonReactive = markNonReactive;\nexports.markReadonly = markReadonly;\nexports.mergeProps = mergeProps;\nexports.nextTick = nextTick;\nexports.onActivated = onActivated;\nexports.onBeforeMount = onBeforeMount;\nexports.onBeforeUnmount = onBeforeUnmount;\nexports.onBeforeUpdate = onBeforeUpdate;\nexports.onDeactivated = onDeactivated;\nexports.onErrorCaptured = onErrorCaptured;\nexports.onMounted = onMounted;\nexports.onRenderTracked = onRenderTracked;\nexports.onRenderTriggered = onRenderTriggered;\nexports.onUnmounted = onUnmounted;\nexports.onUpdated = onUpdated;\nexports.openBlock = openBlock;\nexports.popScopeId = popScopeId;\nexports.provide = provide;\nexports.pushScopeId = pushScopeId;\nexports.reactive = reactive;\nexports.readonly = readonly;\nexports.recordEffect = recordEffect;\nexports.ref = ref;\nexports.registerRuntimeCompiler = registerRuntimeCompiler;\nexports.render = render;\nexports.renderList = renderList;\nexports.renderSlot = renderSlot;\nexports.resolveComponent = resolveComponent;\nexports.resolveDirective = resolveDirective;\nexports.resolveDynamicComponent = resolveDynamicComponent;\nexports.resolveTransitionHooks = resolveTransitionHooks;\nexports.setBlockTracking = setBlockTracking;\nexports.setTransitionHooks = setTransitionHooks;\nexports.toHandlers = toHandlers;\nexports.toRaw = toRaw;\nexports.toRefs = toRefs;\nexports.toString = toString;\nexports.useCSSModule = useCSSModule;\nexports.useTransitionState = useTransitionState;\nexports.vModelCheckbox = vModelCheckbox;\nexports.vModelDynamic = vModelDynamic;\nexports.vModelRadio = vModelRadio;\nexports.vModelSelect = vModelSelect;\nexports.vModelText = vModelText;\nexports.vShow = vShow;\nexports.version = version;\nexports.warn = warn;\nexports.watch = watch;\nexports.withDirectives = withDirectives;\nexports.withKeys = withKeys;\nexports.withModifiers = withModifiers;\nexports.withScopeId = withScopeId;\n```\n\n我们此时可以看看 `vue-next/packages/vue/examples` 文件夹里面的几个示例。 --\u003e\n\n\n# License\n\nCopyright(C) 2019, [Vue Cli](https://github.com/Wscats/vue-cli) is released under the [MIT](http://opensource.org/licenses/MIT).","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwscats%2Fvue-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwscats%2Fvue-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwscats%2Fvue-cli/lists"}