├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── README.md ├── assets ├── Snip20201215_6.png └── old.md ├── demo ├── api │ ├── base-api.html │ ├── compositions.html │ └── options.html ├── compositions-api │ ├── button.html │ └── useMouse.html ├── helloworld │ ├── composition.html │ └── options.html ├── reactivity-demo │ ├── __test__ │ │ ├── vue2.spec.js │ │ └── vue3.spec.js │ ├── vue2.js │ └── vue3.js ├── vue-cli-2 │ ├── .browserslistrc │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ └── src │ │ ├── App.vue │ │ ├── assets │ │ └── logo.png │ │ ├── components │ │ └── HelloWorld.vue │ │ ├── main.js │ │ ├── router │ │ └── index.js │ │ ├── store │ │ └── index.js │ │ └── views │ │ ├── About.vue │ │ └── Home.vue └── vue-cli-3 │ ├── .gitignore │ ├── README.md │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── main.js │ ├── router │ │ └── index.js │ ├── store │ │ └── index.js │ └── views │ │ ├── About.vue │ │ └── Home.vue │ └── vue.config.js ├── mini-rollup ├── acorn │ ├── index.js │ ├── scope.js │ └── walk.js ├── bundle.js ├── debugger.js ├── lib │ ├── ast │ │ ├── analyse.js │ │ ├── scope.js │ │ └── walk.js │ ├── bundle.js │ ├── example.js │ ├── module.js │ └── rollup.js ├── magic-string.js ├── package.json ├── src │ ├── c.js │ ├── foo.js │ └── main.js └── yarn.lock ├── mini-vite ├── index.html ├── index.js ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── index.css │ └── main.js └── yarn.lock ├── mini-vue-plus ├── .vscode │ └── settings.json ├── App.js ├── core │ ├── h.js │ ├── index.js │ ├── reactivity │ │ └── index.js │ └── renderer │ │ └── index.js ├── index.html ├── index.js └── package.json ├── mini-vue ├── 00_original.html ├── 01_mvvm.html ├── 02_reactivity.html ├── 03-render.html ├── compiler │ ├── ast.js │ └── simple.js ├── complete │ ├── App.js │ ├── core │ │ ├── h.js │ │ ├── index.js │ │ ├── reactivity │ │ │ └── index.js │ │ └── renderer.js │ ├── index.html │ └── main.js ├── options_api │ ├── 00_original.html │ ├── 01_mvvm.html │ └── 02_reactivity.html ├── package.json ├── reactivity │ ├── __test__ │ │ └── index.spec.js │ └── index.js └── runtime │ ├── diff │ ├── __tests__ │ │ └── index.spec.js │ └── index.js │ ├── h.js │ └── renderer.js ├── mini-vuepress ├── README.md ├── index.js ├── markdown │ ├── __tests__ │ │ ├── index.spec.js │ │ ├── test.css │ │ └── test.md │ ├── index.js │ └── mark.css ├── package.json ├── scaner.js ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── index.css │ └── main.js ├── ssr │ ├── App.vue │ ├── __tests__ │ │ └── index.spec.js │ ├── index.html │ ├── index.js │ ├── list.js │ └── server.js ├── template │ └── App.vue ├── vite │ ├── README.md │ ├── index.html │ └── index.js └── yarn.lock ├── petitevue └── counter │ ├── composition.html │ ├── options.html │ ├── petite.html │ └── vuex.html ├── scalingup ├── Vue3规模化.md ├── router │ └── index.html ├── ssr │ ├── App.vue │ ├── index.js │ └── package.json └── state │ └── index.html ├── toolchain ├── babel │ └── index.js ├── package.json ├── yarn-error.log └── yarn.lock ├── vue-mastery ├── composition-api │ ├── README.md │ ├── demo │ │ ├── .gitignore │ │ ├── README.assets │ │ │ ├── image-20201206165135473.png │ │ │ ├── image-20201206171613601.png │ │ │ ├── image-20201206172905686.png │ │ │ └── image-20201206174225781.png │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── favicon.ico │ │ └── src │ │ │ ├── App.vue │ │ │ ├── assets │ │ │ ├── flower.webm │ │ │ └── logo.png │ │ │ ├── components │ │ │ ├── Computed.vue │ │ │ ├── LifecycleHooks.js │ │ │ ├── Method.vue │ │ │ ├── Modularizing │ │ │ │ ├── Modularzing.vue │ │ │ │ └── index.js │ │ │ ├── Setup.vue │ │ │ ├── SharingState │ │ │ │ ├── Origin.vue │ │ │ │ ├── SharingState.vue │ │ │ │ ├── index.js │ │ │ │ └── usePromise.js │ │ │ ├── Suspense │ │ │ │ ├── Event.vue │ │ │ │ ├── Suspense.vue │ │ │ │ └── index.js │ │ │ ├── Teleport.vue │ │ │ ├── Watch.vue │ │ │ ├── composables │ │ │ │ └── use-event-space.js │ │ │ └── reactiveSynatx.vue │ │ │ ├── index.css │ │ │ └── main.js │ └── sub │ │ ├── 01_composition_api.srt │ │ ├── 02_setup&ref.srt │ │ ├── 03_methods.srt │ │ ├── 04_computedproperties.srt │ │ ├── 05_reactivesynatx.srt │ │ ├── 06_modularizing.srt │ │ ├── 07_lifecyclehooks.srt │ │ ├── 08_watch.srt │ │ ├── 09_sharingstate.srt │ │ ├── 10_suspense.srt │ │ └── 11_teleport.srt └── deep-dive │ ├── README.md │ └── demo │ ├── 13 │ ├── ScopeSlots.html │ ├── compositionAPI.html │ ├── higherOrderCompent.html │ ├── mouse.html │ ├── reusable.html │ └── vue-mastery-DiveP13.md │ ├── 03 │ ├── render-fn.js │ └── stack.html │ ├── 04 │ ├── render-fn.js │ └── vdom.html │ └── reactivity.html └── vuex ├── 01-introduction └── cdn.html ├── 02-core-concepts ├── 01-state.html ├── 02-getter.html ├── 03-mutation.html ├── 04-action.html └── 05-module │ ├── 01-basic.html │ ├── 02-namespace.html │ └── 03-register.html ├── 03-advanced ├── 02-composition-api.html ├── 03-plugins │ ├── 01-helloworld.html │ └── 02-websocket │ │ ├── index.html │ │ ├── index.js │ │ └── package.json ├── 04-strict-mode │ └── index.html ├── 05-form │ └── index.html ├── 06-testing │ ├── api │ │ └── shop.js │ ├── demo │ │ ├── moduleA.js │ │ └── test.js │ ├── index.html │ ├── package.json │ ├── store │ │ ├── __tests__ │ │ │ ├── actions.spec.js │ │ │ ├── getters.spec.js │ │ │ ├── hello.js │ │ │ ├── index.js │ │ │ └── mutations.spec.js │ │ ├── actions.js │ │ ├── getters.js │ │ └── store.js │ ├── webpack.config.js │ ├── webpack.dev.config.js │ ├── yarn-error.log │ └── yarn.lock └── 08-typescript │ ├── .browserslistrc │ ├── .gitignore │ ├── 02-composition-api.html │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── main.ts │ ├── router │ │ └── index.ts │ ├── shims-vue.d.ts │ ├── store │ │ └── index.ts │ ├── views │ │ ├── About.vue │ │ └── Home.vue │ └── vuex.d.ts │ ├── tsconfig.json │ └── yarn.lock ├── README.md └── source ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── Getter.js │ ├── HelloWorld.js │ ├── Mutation.js │ └── State.js └── main.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | package-lock.json 3 | **/.DS_Store 4 | **/dist/ 5 | webpack/bundle.js 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "source/vue-next"] 2 | path = source/vue-next 3 | url = https://github.com/vuejs/vue-next 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | 然叔的Vue3.0学习笔记 4 | 5 | ## 🏠欢迎一起交流 6 | 微信公众号 🔍【前端大班车】 ,多位【🔥Vue3代码贡献者】带你飞🚀 7 | 8 | ## 目录结构 9 | 10 | ``` 11 | . 12 | ├── mini-vite // 简写版Vite 13 | ├── mini-webpack // 简写版webpack 14 | ├── mini-vue // 简写版Vue 15 | ├── mini-vue-plus // Vue(B站天天造轮子版) 16 | ├── vue-mastery // Vue-Mastery 内容demo 17 | ├── template-explorer // Vue3模板编译调试工具 18 | ├── demo // Vue基础APIDemo 19 | ├── source // vue3源码 submodule 20 | └── vue-next-cli-demo // Vue3 CLI工具Demo 21 | ``` 22 | 23 | 24 | 25 | 26 | 27 | ## 掘金和语雀精华 28 | 29 | ![image-20201215174235942](https://gitee.com/josephxia/picgo/raw/master/juejin/image-20201215174235942.png) 30 | 31 | [📖语雀 - VueMastery学习笔记](https://www.yuque.com/nxtt7g/kompdt) 32 | 33 | 34 | 35 | - [Vue3.0全球发布会干货总结](https://juejin.cn/post/6875236411349008398) 36 | - [如何参加开源项目-如何给Vue3.0提PR](https://juejin.cn/post/6844904191744278542) 37 | - [跟我一起编写Vue3版ElementUI](https://juejin.cn/post/6864462363039531022) 38 | 39 | - [忙了一夜用CompositionAPI征服产品妹子花里胡哨的需求](https://juejin.cn/post/6891885484524437518) 40 | 41 | - [闪电五连鞭:Composition API原理深度剖析](https://juejin.cn/post/6894993303486332941) 42 | - [又是一夜,这篇Composition-API实操还觉得短吗](https://juejin.cn/post/6892017198450081800) 43 | - [拿下vue3你要做好这些准备](https://juejin.cn/post/6866373381424414734) 44 | 45 | 46 | ## B站热门视频教程 47 | - [【全网首发】Vue3.0光速上手「持续更新中」](https://www.bilibili.com/video/BV1Wh411X7Xp) 48 | 49 | - [【Vue Mastery】composition API + 深度解读](https://www.bilibili.com/video/BV1my4y1m7sz) 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /assets/Snip20201215_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/assets/Snip20201215_6.png -------------------------------------------------------------------------------- /assets/old.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ## 参考资料 7 | 8 | ### 试用Vue3 9 | 10 | > 参考资料 https://blog.csdn.net/guotianqing/article/details/82391665 11 | 12 | ### 初始化子模块 13 | 14 | ```bash 15 | git submodule add https://github.com/vuejs/vue-next source/vue-next 16 | ``` 17 | 18 | 子模块内容记录在.gitmodules文件中 19 | 20 | ```bash 21 | # 初始化子模块 22 | git submodule init 23 | # 更新模块 24 | git submodule update --init --recursive 25 | ``` 26 | 27 | ### 安装依赖 28 | 29 | ``` bash 30 | ## 修改镜像 31 | yarn config set registry https://registry.npm.taobao.org --global 32 | yarn config set disturl https://npm.taobao.org/dist --global 33 | 34 | ## 去除pupteer 35 | # 忽略下载Chromium 36 | cd source/vue-next 37 | ## 去除pupteer 38 | yarn --ignore-scripts 39 | 40 | ``` 41 | 42 | ### 编译Build 43 | 44 | ``` bash 45 | cd source/vue-next 46 | yarn build 47 | ``` 48 | 49 | ### 调试Vue代码 50 | 51 | ``` bash 52 | cd source/vue-next 53 | yarn build 54 | ``` 55 | 56 | ### 测试API 57 | 58 | -------------------------------------------------------------------------------- /demo/api/compositions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /demo/api/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /demo/compositions-api/button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 17 | 18 | 19 | 20 |
21 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /demo/compositions-api/useMouse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /demo/helloworld/composition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /demo/helloworld/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 |
13 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /demo/reactivity-demo/__test__/vue2.spec.js: -------------------------------------------------------------------------------- 1 | const { reactive, effect } = require('../vue2') 2 | 3 | describe('reactivity/vue2', () => { 4 | 5 | it('测试数据改变时 是否被响应', () => { 6 | const data = reactive({ 7 | name: 'abc', 8 | age: { 9 | n: 5 10 | } 11 | }) 12 | // Mock一个响应函数 13 | const fn = jest.fn() 14 | const result = fn() 15 | 16 | // 设置响应函数 17 | effect(fn) 18 | 19 | // 改变数据 20 | data.name = 'efg' 21 | 22 | // 确认fn生效 23 | expect(fn).toBeCalled() 24 | }) 25 | 26 | 27 | it('测试多层数据中改变时 是否被响应', () => { 28 | const data = reactive({ 29 | age: { 30 | n: 5 31 | } 32 | }) 33 | // Mock一个响应函数 34 | const fn = jest.fn() 35 | 36 | // 设置响应函数 37 | effect(fn) 38 | 39 | // 改变多层数据 40 | data.age.n = 1 41 | 42 | // 确认fn生效 43 | expect(fn).toBeCalled() 44 | }) 45 | 46 | 47 | it('测试数组中数据改变时 是否被响应', () => { 48 | const data = reactive({ 49 | ary: [ 50 | 'a' 51 | ] 52 | }) 53 | // Mock一个响应函数 54 | const fn = jest.fn() 55 | 56 | // 设置响应函数 57 | effect(fn) 58 | 59 | // 改变多层数据 60 | data.ary.push('b') 61 | 62 | // 确认fn生效 63 | expect(fn).toBeCalled() 64 | }) 65 | }) -------------------------------------------------------------------------------- /demo/reactivity-demo/__test__/vue3.spec.js: -------------------------------------------------------------------------------- 1 | const { reactive, effect } = require('../vue3') 2 | 3 | // 真正的源码 4 | // const { 5 | // reactive, 6 | // effect 7 | // } = require('../../../packages/reactivity/index') 8 | 9 | describe('reactivity/vue3', () => { 10 | 11 | it('测试数据改变时 是否被响应', () => { 12 | const data = reactive({ 13 | name: 'abc', 14 | age: { 15 | n: 5 16 | } 17 | }) 18 | // Mock一个响应函数 19 | const fn = jest.fn() 20 | const result = fn() 21 | 22 | // 设置响应函数 23 | effect(fn) 24 | 25 | // 改变数据 26 | data.name = 'efg' 27 | 28 | // 确认fn生效 29 | expect(fn).toBeCalled() 30 | }) 31 | 32 | 33 | it('测试多层数据中改变时 是否被响应', () => { 34 | const data = reactive({ 35 | age: { 36 | n: 5 37 | } 38 | }) 39 | // Mock一个响应函数 40 | const fn = jest.fn() 41 | 42 | // 设置响应函数 43 | effect(fn) 44 | 45 | // 改变多层数据 46 | data.age.n = 1 47 | 48 | // 确认fn生效 49 | expect(fn).toBeCalled() 50 | }) 51 | 52 | 53 | it('测试数组中数据改变时 是否被响应', () => { 54 | const data = reactive({ 55 | ary: [ 56 | 'a' 57 | ] 58 | }) 59 | // Mock一个响应函数 60 | const fn = jest.fn() 61 | 62 | // 设置响应函数 63 | effect(fn) 64 | 65 | // 改变多层数据 66 | data.ary.push('b') 67 | 68 | // 确认fn生效 69 | expect(fn).toBeCalled() 70 | }) 71 | }) -------------------------------------------------------------------------------- /demo/reactivity-demo/vue2.js: -------------------------------------------------------------------------------- 1 | let effective 2 | function effect(fun) { 3 | effective = fun 4 | } 5 | 6 | const oldArrayPrototype = Array.prototype 7 | const proto = Object.create(oldArrayPrototype); 8 | 9 | ['push','pop','shift','unshift','splice','sort','reverse'].forEach(method => { 10 | 11 | // 函数劫持 12 | proto[method] = function(){ 13 | effective() 14 | oldArrayPrototype[method].call(this,...arguments) 15 | } 16 | }) 17 | 18 | 19 | function reactive(data) { 20 | if (typeof data !== 'object' || data === null) { 21 | return data 22 | } 23 | 24 | // 数组通过数据劫持提供响应式 25 | if(Array.isArray(data)){ 26 | data.__proto__ = proto 27 | } 28 | 29 | Object.keys(data).forEach(function (key) { 30 | let value = data[key] 31 | // 递归调用 32 | reactive(value) 33 | Object.defineProperty(data, key, { 34 | emumerable: false, 35 | configurable: true, 36 | get: () => { 37 | return value 38 | }, 39 | set: newVal => { 40 | if (newVal !== value) { 41 | effective() 42 | value = newVal 43 | } 44 | } 45 | }) 46 | 47 | }) 48 | return data 49 | } 50 | 51 | module.exports = { 52 | effect, reactive 53 | } 54 | 55 | -------------------------------------------------------------------------------- /demo/reactivity-demo/vue3.js: -------------------------------------------------------------------------------- 1 | // const { 2 | // reactive, 3 | // effect 4 | // } = require('../../packages/reactivity/index') 5 | 6 | let effective 7 | function effect(fun) { 8 | effective = fun 9 | } 10 | 11 | function reactive(data) { 12 | if (typeof data !== 'object' || data === null) { 13 | return data 14 | } 15 | const observed = new Proxy(data, { 16 | get(target, key, receiver) { 17 | // 普通写法 18 | // return target[key] 19 | // proxy + reflect 反射 20 | // Reflect有返回值不报错 21 | let result = Reflect.get(target, key, receiver) 22 | 23 | // return result 24 | // 多层代理 25 | return typeof result !== 'object' ? result : reactive(result) 26 | }, 27 | set(target, key, value, receiver) { 28 | effective() 29 | // 普通写法 30 | // target[key] = value // 如果设置不成功 没有返回 31 | // proxy + reflect 32 | const ret = Reflect.set(target, key, value, receiver) 33 | return ret 34 | }, 35 | 36 | deleteProperty(target,key){ 37 | const ret = Reflect.deleteProperty(target,key) 38 | return ret 39 | } 40 | 41 | }) 42 | return observed 43 | } 44 | 45 | module.exports = { 46 | reactive, effect 47 | } 48 | 49 | 50 | 51 | // // 设置数据响应 52 | // const data = reactive({ 53 | // name: 'abc' 54 | // }) 55 | // // 数据响应 56 | // effect(() => { 57 | // console.log('effect....', data.name) 58 | // }) 59 | 60 | // // 修改数据 61 | // data.name = 'efg' 62 | -------------------------------------------------------------------------------- /demo/vue-cli-2/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /demo/vue-cli-2/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /demo/vue-cli-2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /demo/vue-cli-2/README.md: -------------------------------------------------------------------------------- 1 | # vue-cli-router 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /demo/vue-cli-2/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /demo/vue-cli-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.4", 12 | "vue": "^2.6.11", 13 | "vue-router": "^3.1.6", 14 | "vuex": "^3.1.3" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "~4.3.0", 18 | "@vue/cli-plugin-eslint": "~4.3.0", 19 | "@vue/cli-plugin-router": "~4.3.0", 20 | "@vue/cli-plugin-vuex": "~4.3.0", 21 | "@vue/cli-service": "~4.3.0", 22 | "babel-eslint": "^10.1.0", 23 | "eslint": "^6.7.2", 24 | "eslint-plugin-vue": "^6.2.2", 25 | "vue-template-compiler": "^2.6.11" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/vue-cli-2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/demo/vue-cli-2/public/favicon.ico -------------------------------------------------------------------------------- /demo/vue-cli-2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/demo/vue-cli-2/src/assets/logo.png -------------------------------------------------------------------------------- /demo/vue-cli-2/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 45 | 46 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | Vue.config.productionTip = false 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | render: h => h(App) 12 | }).$mount('#app') 13 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Home from '../views/Home.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | name: 'Home', 11 | component: Home 12 | }, 13 | { 14 | path: '/about', 15 | name: 'About', 16 | // route level code-splitting 17 | // this generates a separate chunk (about.[hash].js) for this route 18 | // which is lazy-loaded when the route is visited. 19 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 20 | } 21 | ] 22 | 23 | const router = new VueRouter({ 24 | mode: 'history', 25 | base: process.env.BASE_URL, 26 | routes 27 | }) 28 | 29 | export default router 30 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | count:1 9 | }, 10 | mutations: { 11 | add(state){ 12 | state.count ++ 13 | } 14 | }, 15 | actions: { 16 | }, 17 | modules: { 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /demo/vue-cli-2/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /demo/vue-cli-3/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /demo/vue-cli-3/README.md: -------------------------------------------------------------------------------- 1 | # vue-cli-3 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /demo/vue-cli-3/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /demo/vue-cli-3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli-3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.4", 12 | "vue": "^3.0.0-beta.1", 13 | "vue-router": "^4.0.0-alpha.5", 14 | "vuex": "^4.0.0-alpha.1" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "~4.3.0", 18 | "@vue/cli-plugin-eslint": "~4.3.0", 19 | "@vue/cli-plugin-router": "~4.3.0", 20 | "@vue/cli-plugin-vuex": "~4.3.0", 21 | "@vue/cli-service": "~4.3.0", 22 | "@vue/compiler-sfc": "^3.0.0-beta.1", 23 | "babel-eslint": "^10.1.0", 24 | "eslint": "^6.7.2", 25 | "eslint-plugin-vue": "^7.0.0-alpha.0", 26 | "vue-cli-plugin-vue-next": "~0.1.2" 27 | }, 28 | "eslintConfig": { 29 | "root": true, 30 | "env": { 31 | "node": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/vue3-essential", 35 | "eslint:recommended" 36 | ], 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | }, 40 | "rules": {} 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions", 45 | "not dead" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /demo/vue-cli-3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/demo/vue-cli-3/public/favicon.ico -------------------------------------------------------------------------------- /demo/vue-cli-3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/demo/vue-cli-3/src/assets/logo.png -------------------------------------------------------------------------------- /demo/vue-cli-3/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 45 | 46 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | createApp(App).use(router).use(store).mount('#app') 7 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router'; 2 | import Home from '../views/Home.vue' 3 | 4 | const routes = [ 5 | { 6 | path: '/', 7 | name: 'Home', 8 | component: Home 9 | }, 10 | { 11 | path: '/about', 12 | name: 'About', 13 | // route level code-splitting 14 | // this generates a separate chunk (about.[hash].js) for this route 15 | // which is lazy-loaded when the route is visited. 16 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 17 | } 18 | ] 19 | 20 | const router = createRouter({ 21 | history: createWebHistory(process.env.BASE_URL), 22 | routes 23 | }) 24 | 25 | export default router 26 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vuex from 'vuex' 2 | 3 | export default Vuex.createStore({ 4 | state: { 5 | count:1 6 | }, 7 | mutations: { 8 | add(state){ 9 | state.count ++ 10 | } 11 | }, 12 | actions: { 13 | }, 14 | modules: { 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /demo/vue-cli-3/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | -------------------------------------------------------------------------------- /demo/vue-cli-3/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devServer: { 3 | overlay: { 4 | warnings: false, 5 | errors: false 6 | }, 7 | }, 8 | lintOnSave: false 9 | } 10 | -------------------------------------------------------------------------------- /mini-rollup/acorn/index.js: -------------------------------------------------------------------------------- 1 | const acorn = require("acorn"); 2 | 3 | const walk = require("./walk"); 4 | const code = 5 | ` 6 | import { a } from "./foo"; 7 | console.log("Hello" + a); 8 | console.log("World"); 9 | export const b = 1 10 | 11 | ` 12 | 13 | let ast = acorn.parse(code, { 14 | locations: true, // 索引位置 15 | ranges: true, 16 | sourceType: "module", 17 | ecmaVersion: 7, 18 | }); 19 | 20 | let indent = 0; 21 | const padding = () => " ".repeat(indent); 22 | 23 | // 遍历语法树中的每一条语句 由walk遍历子元素 24 | // 深度优先原则 25 | ast.body.forEach((statement) => { 26 | walk(statement, { 27 | enter(node) { 28 | if (node.type) { 29 | console.log(padding() + node.type + ' enter'); 30 | indent += 2; 31 | } 32 | }, 33 | leave(node) { 34 | if (node.type) { 35 | indent -= 2; 36 | console.log(padding() + node.type+ ' leave'); 37 | } 38 | }, 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /mini-rollup/acorn/scope.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | class Scope { 5 | constructor(options = {}) { 6 | this.name = options.name; 7 | this.parent = options.parent; 8 | this.names = options.params || []; // 此作用域内变量 9 | } 10 | 11 | add(name) { 12 | this.names.push(name); 13 | } 14 | 15 | /** 16 | * 找出作用域声明位置 17 | * @param {*} name 18 | */ 19 | findDefiningScope(name) { 20 | if (this.names.includes(name)) { 21 | return this; 22 | } 23 | if (this.parent) { 24 | return this.parent.findDefiningScope(name); 25 | } 26 | return null; 27 | } 28 | } 29 | 30 | let a = 1; 31 | function one() { 32 | let b = 2; 33 | function two(age) { 34 | let c = 3; 35 | console.log(a, b, c, age); 36 | } 37 | two(); 38 | } 39 | one(); 40 | 41 | let globalScope = new Scope({ 42 | name: "Global", 43 | params: [], 44 | parent: null, 45 | }); 46 | 47 | globalScope.add("a"); 48 | let oneScope = new Scope({ 49 | name: "oneScope", 50 | params: [], 51 | parent: globalScope, 52 | }); 53 | 54 | oneScope.add('b') 55 | let twoScope = new Scope({ 56 | name:'twoScope', params: ['ago'], parent: oneScope 57 | }) 58 | twoScope.add('c') 59 | 60 | let aScope = twoScope.findDefiningScope('a') 61 | console.log(aScope.name) 62 | 63 | let bScope = twoScope.findDefiningScope('b') 64 | console.log(bScope.name) 65 | 66 | let cScope = twoScope.findDefiningScope('c') 67 | console.log(cScope.name) 68 | 69 | 70 | let ageScope = twoScope.findDefiningScope('age') 71 | console.log(ageScope.name) 72 | 73 | 74 | // tree-shaking原理就是基于作用域链 75 | 76 | 77 | module.exports = Scope; 78 | -------------------------------------------------------------------------------- /mini-rollup/acorn/walk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AST语法树遍历 3 | */ 4 | function walk(ast, { enter, leave }) { 5 | visit(ast, null, enter, leave); 6 | } 7 | 8 | function visit(node, parent, enter, leave) { 9 | if(!node) return 10 | 11 | // 先执行enter 12 | if (enter) { 13 | enter.call(null, node, parent); 14 | } 15 | 16 | let childkeys = Object.keys(node).filter( 17 | (key) => typeof node[key] === "object" 18 | ); 19 | 20 | childkeys.forEach(childKey => { 21 | let value = node[childKey] 22 | if(Array.isArray(val => visit(val,node,enter,leave))){ 23 | value.forEach(val => visit(val,node,enter,leave)) 24 | }else { 25 | visit(value,node,enter,leave) 26 | } 27 | }) 28 | 29 | if (leave) { 30 | leave(node, parent); 31 | } 32 | } 33 | 34 | module.exports = walk; 35 | -------------------------------------------------------------------------------- /mini-rollup/bundle.js: -------------------------------------------------------------------------------- 1 | console.log('abc') 2 | const c = 'c' 3 | console.log("Hello" + a); 4 | const b = "b"; 5 | console.log("World"+ b); -------------------------------------------------------------------------------- /mini-rollup/debugger.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const rollup = require('./lib/rollup') 3 | // 入口文件的绝对路径 4 | let entry = path.resolve(__dirname,'src/main.js') 5 | rollup(entry,'bundle.js') -------------------------------------------------------------------------------- /mini-rollup/lib/ast/analyse.js: -------------------------------------------------------------------------------- 1 | const Scope = require("./scope"); 2 | const walk = require("./walk"); 3 | /** 4 | * 当前模块使用哪些变量 5 | * 哪些变量是当前模块声明 6 | * 7 | * @param {*} ast 8 | * @param {*} magicStirng 9 | * @param {*} module 10 | */ 11 | function analyse(ast, magicStirng, module) { 12 | // 创建全局作用域 13 | let scope = new Scope(); 14 | // 遍历当前语法树 15 | ast.body.forEach((statement) => { 16 | 17 | // 给作用域内添加变量 18 | function addToScope(declaration) { 19 | var name = declaration.id.name; // 获取声明的变量 20 | scope.add(name); 21 | if (!scope.parent) { // 如果此变量作用域不在父级作用域 即当前作用域 22 | // 如果当前是全局作用域的话 23 | // 在全局作用域下声明全局变量 24 | statement._defines[name] = true; 25 | } 26 | } 27 | 28 | Object.defineProperties(statement, { 29 | _defines: { value: {} }, // 当前模块定义的所有全局变量 30 | 31 | _dependsOn: { value: {} }, // 当前模块没有定义但是使用过的变量 32 | 33 | _included: { value: false, writable: true }, // 此语句是否被打包 防止多次打包 34 | 35 | _source: { value: magicStirng.snip(statement.start, statement.end) }, 36 | }); 37 | // console.log('sate',statement) 38 | // 投建我们的作用域链 39 | walk(statement, { 40 | enter(node) { 41 | let newScope; 42 | if(node === null || node.length === 0) return 43 | console.log('walk', node.type) 44 | switch (node.type) { 45 | case "FunctionDeclaration": 46 | const params = node.params.map((x) => x.name); 47 | addToScope(node); 48 | // 新作用域 49 | newScope = new Scope({ 50 | parent: scope, 51 | params, 52 | }); 53 | break; 54 | 55 | case "VariableDeclaration": 56 | node.declarations.forEach(addToScope); 57 | break; 58 | } 59 | if (newScope) { 60 | // 当前节点声明的新作用域 61 | // 如果此节点生成一个新作用域 62 | Object.defineProperties(node, "_scope", { value: newScope }); 63 | scope = newScope; 64 | } 65 | }, 66 | leave(node) { 67 | if (node._scope) { 68 | // 如果此节点离开退回父作用域 69 | scope = scope.parent; 70 | } 71 | }, 72 | }); 73 | }); 74 | 75 | ast._scope = scope; 76 | // 找出外部依赖 dependsOn 77 | ast.body.forEach((statement) => { 78 | walk(statement, { 79 | enter(node) { 80 | if (node._scope) { 81 | scope = node._scope; 82 | } 83 | if (node.type === "Identifier") { 84 | // 向上递归 85 | const definingScope = scope.findDefiningScope(node.name); 86 | if (!definingScope) { 87 | statement._dependsOn[node.name] = true; // 表示属于外部依赖变量 88 | } 89 | } 90 | }, 91 | 92 | leave(node) { 93 | if (node._scope) scope = scope.parent; 94 | }, 95 | }); 96 | }); 97 | 98 | 99 | 100 | // 全量的代码 101 | // ast.body.forEach((statement) => { 102 | // console.log('statement',statement) 103 | // Object.defineProperties(statement, { 104 | // // start在节点中的起始索引 和结束索引 105 | // _source: { value: magicStirng.snip(statement.start, statement.end) }, 106 | // }); 107 | // }); 108 | } 109 | 110 | module.exports = analyse; 111 | -------------------------------------------------------------------------------- /mini-rollup/lib/ast/scope.js: -------------------------------------------------------------------------------- 1 | class Scope { 2 | constructor(options = {}) { 3 | this.parent = options.parent // 父作用域 4 | this.depth = this.parent ? this.parent.depth + 1 : 0 // 作用域层级 5 | this.names = options.params || [] // 作用域内的变量 6 | this.isBlockScope = !!options.block // 是否块作用域 7 | } 8 | 9 | add(name, isBlockDeclaration) { 10 | if (!isBlockDeclaration && this.isBlockScope) { 11 | // it's a `var` or function declaration, and this 12 | // is a block scope, so we need to go up 13 | this.parent.add(name, isBlockDeclaration) 14 | } else { 15 | this.names.push(name) 16 | } 17 | } 18 | 19 | contains(name) { 20 | return !!this.findDefiningScope(name) 21 | } 22 | 23 | findDefiningScope(name) { 24 | if (this.names.includes(name)) { 25 | return this 26 | } 27 | 28 | if (this.parent) { 29 | return this.parent.findDefiningScope(name) 30 | } 31 | 32 | return null 33 | } 34 | } 35 | 36 | module.exports = Scope -------------------------------------------------------------------------------- /mini-rollup/lib/ast/walk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AST语法树遍历 3 | */ 4 | function walk(ast, { enter, leave }) { 5 | visit(ast, null, enter, leave); 6 | } 7 | 8 | function visit(node, parent, enter, leave) { 9 | if(!node) return 10 | // 先执行enter 11 | if (enter) { 12 | enter.call(null, node, parent); 13 | } 14 | 15 | let childkeys = Object.keys(node).filter( 16 | (key) => typeof node[key] === "object" 17 | ); 18 | 19 | childkeys.forEach((childKey) => { 20 | let value = node[childKey]; 21 | if (Array.isArray((val) => visit(val, node, enter, leave))) { 22 | value.forEach((val) => visit(val, node, enter, leave)); 23 | } else { 24 | visit(value, node, enter, leave); 25 | } 26 | }); 27 | 28 | if (leave) { 29 | leave(node, parent); 30 | } 31 | } 32 | 33 | module.exports = walk; 34 | -------------------------------------------------------------------------------- /mini-rollup/lib/bundle.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require('path') 3 | const Module = require("./module"); 4 | const { default: MagicString } = require("magic-string"); 5 | class Bundle { 6 | constructor(options) { 7 | // TODO 正则部分可以消除 8 | // 入口文件的绝对路径 9 | this.entryPath = options.entry.replace(/\.js$/, "") + ".js"; 10 | 11 | this.modules = []; // 存放着所有的模块 入口文件和依赖的模块 12 | } 13 | 14 | build(outputFileName) { 15 | // 从入口的绝对路径找到模块定义 16 | let entryModule = this.fetchModule(this.entryPath); 17 | 18 | // 把入口所有的语句展开 返回语句数组 19 | // import {name,age} 20 | this.statements = entryModule.expandAllStatements(); 21 | 22 | // 生成代码 23 | const { code } = this.generate(); 24 | 25 | console.log("==========output============="); 26 | console.log(code); 27 | fs.writeFileSync(outputFileName, code, "utf-8"); 28 | } 29 | 30 | generate() { 31 | const magicString = new MagicString.Bundle(); 32 | this.statements.forEach((statement) => { 33 | const source = statement._source.clone(); 34 | if(statement.type === "ExportNamedDeclaration") { 35 | source.remove(statement.start,statement.declaration.start) 36 | } 37 | magicString.addSource({ 38 | content: source, 39 | separator: "\n", 40 | }); 41 | }); 42 | return { code: magicString.toString() }; 43 | } 44 | 45 | /** 46 | * 获取模块信息 47 | */ 48 | fetchModule(importee, importer) { 49 | // const route = importee; // 入口文件的绝对路径 50 | let route 51 | if (!importer) { 52 | route = importee; 53 | } else { 54 | if (path.isAbsolute(importee)) { 55 | route = importee; 56 | // } else if (importee[0] == "") { 57 | }else{ 58 | // 相对路径 59 | route = path.resolve( 60 | path.dirname(importer), 61 | importee.replace(/\.js$/, "") + ".js" 62 | ); 63 | // console.log('route',route) 64 | } 65 | } 66 | if (route) { 67 | // 读代码 68 | const code = fs.readFileSync(route, "utf-8").toString(); 69 | const module = new Module({ 70 | code, 71 | path: route, // 模块的绝对路径 72 | bundle: this, // 上下文 73 | }); 74 | return module; 75 | } 76 | } 77 | } 78 | 79 | module.exports = Bundle; 80 | -------------------------------------------------------------------------------- /mini-rollup/lib/example.js: -------------------------------------------------------------------------------- 1 | import {name , age} from './msg' // 忽略 2 | 3 | 4 | function say() { 5 | console.log('say') 6 | } 7 | say() 8 | console.log(age) -------------------------------------------------------------------------------- /mini-rollup/lib/rollup.js: -------------------------------------------------------------------------------- 1 | const Bundle = require("./bundle"); 2 | 3 | function rollup(entry, outputFileName) { 4 | // 打包对象 5 | const bundle = new Bundle({ entry }); 6 | // 调用build方法进行编译 7 | bundle.build(outputFileName); 8 | } 9 | 10 | module.exports = rollup; 11 | -------------------------------------------------------------------------------- /mini-rollup/magic-string.js: -------------------------------------------------------------------------------- 1 | var MagicString = require( 'magic-string' ); 2 | var s = new MagicString( 'problems = 99' ); 3 | 4 | s.overwrite( 0, 8, 'answer' ); 5 | console.log(s.toString()) 6 | // 'answer = 99' 7 | 8 | s.overwrite( 11, 13, '42' ); // character indices always refer to the original string 9 | s.toString(); // 'answer = 42' 10 | 11 | s.prepend( 'var ' ).append( ';' ); // most methods are chainable 12 | s.toString(); // 'var answer = 42;' 13 | 14 | var map = s.generateMap({ 15 | source: 'source.js', 16 | file: 'converted.js.map', 17 | includeContent: true 18 | }); // generates a v3 sourcemap 19 | 20 | console.log('s:', s.toString()) 21 | console.log('map:', map.toString()) 22 | // require( 'fs' ).writeFile( 'converted.js', s.toString() ); 23 | // require( 'fs' ).writeFile( 'converted.js.map', map.toString() ); 24 | 25 | 26 | s = new MagicString('export var name = "然叔"') 27 | // 截取 28 | console.log(s.snip(0,6).toString()) 29 | 30 | // 删除 31 | console.log(s.remove(0,7).toString()) 32 | 33 | 34 | let bundleString = new MagicString.Bundle() 35 | bundleString.addSource({ 36 | content: 'var a = 1;', 37 | separator:'\n' 38 | }) 39 | 40 | bundleString.addSource({ 41 | content: 'var b = 2;', 42 | separator:'\n' 43 | }) 44 | 45 | console.log(bundleString.toString()) -------------------------------------------------------------------------------- /mini-rollup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-rollup", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "acorn": "^8.3.0", 14 | "magic-string": "^0.25.7", 15 | "rollup": "^2.51.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mini-rollup/src/c.js: -------------------------------------------------------------------------------- 1 | export const c = 'c' -------------------------------------------------------------------------------- /mini-rollup/src/foo.js: -------------------------------------------------------------------------------- 1 | import { c } from "./c"; 2 | export const a = "a" + c; 3 | export const b = "b"; 4 | -------------------------------------------------------------------------------- /mini-rollup/src/main.js: -------------------------------------------------------------------------------- 1 | import { a,b } from "./foo"; 2 | console.log('abc') 3 | console.log("Hello" + a); 4 | console.log("World"+ b); 5 | -------------------------------------------------------------------------------- /mini-rollup/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | acorn@^8.3.0: 6 | version "8.3.0" 7 | resolved "https://registry.nlark.com/acorn/download/acorn-8.3.0.tgz?cache=0&sync_timestamp=1622440246532&other_urls=https%3A%2F%2Fregistry.nlark.com%2Facorn%2Fdownload%2Facorn-8.3.0.tgz#1193f9b96c4e8232f00b11a9edff81b2c8b98b88" 8 | integrity sha1-EZP5uWxOgjLwCxGp7f+Bssi5i4g= 9 | 10 | fsevents@~2.3.1: 11 | version "2.3.2" 12 | resolved "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536512306&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 13 | integrity sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro= 14 | 15 | magic-string@^0.25.7: 16 | version "0.25.7" 17 | resolved "https://registry.npm.taobao.org/magic-string/download/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" 18 | integrity sha1-P0l9b9NMZpxnmNy4IfLvMfVEUFE= 19 | dependencies: 20 | sourcemap-codec "^1.4.4" 21 | 22 | rollup@^2.51.0: 23 | version "2.51.0" 24 | resolved "https://registry.nlark.com/rollup/download/rollup-2.51.0.tgz#ffd847882283998fc8611cd57af917f173b4ab5c" 25 | integrity sha1-/9hHiCKDmY/IYRzVevkX8XO0q1w= 26 | optionalDependencies: 27 | fsevents "~2.3.1" 28 | 29 | sourcemap-codec@^1.4.4: 30 | version "1.4.8" 31 | resolved "https://registry.npm.taobao.org/sourcemap-codec/download/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" 32 | integrity sha1-6oBL2UhXQC5pktBaOO8a41qatMQ= 33 | -------------------------------------------------------------------------------- /mini-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |

然叔 666

11 |
12 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /mini-vite/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Koa = require("koa"); 4 | const compilerSfc = require("@vue/compiler-sfc"); // .vue 5 | const compilerDom = require("@vue/compiler-dom"); // 模板 6 | const app = new Koa(); 7 | function rewriteImport(content) { 8 | return content.replace(/ from ['|"]([^'"]+)['|"]/g, function (s0, s1) { 9 | console.log("s", s0, s1); 10 | // . ../ /开头的,都是相对路径 11 | if (s1[0] !== "." && s1[1] !== "/") { 12 | return ` from '/@modules/${s1}'`; 13 | } else { 14 | return s0; 15 | } 16 | }); 17 | } 18 | 19 | app.use(async (ctx) => { 20 | const { 21 | request: { url, query }, 22 | } = ctx; 23 | console.log("url:" + url, "query type", query.type); 24 | // 首页 25 | if (url == "/") { 26 | ctx.type = "text/html"; 27 | let content = fs.readFileSync("./index.html", "utf-8"); 28 | content = content.replace( 29 | " 34 | -------------------------------------------------------------------------------- /mini-vite/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/mini-vite/src/assets/logo.png -------------------------------------------------------------------------------- /mini-vite/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /mini-vite/src/index.css: -------------------------------------------------------------------------------- 1 | h1{ 2 | color:red; 3 | } 4 | -------------------------------------------------------------------------------- /mini-vite/src/main.js: -------------------------------------------------------------------------------- 1 | console.log('main....') 2 | // alert('2') 3 | 4 | 5 | // import 都会发起一个网络请求 viet拦截这个请求,渲染 6 | import { createApp } from 'vue' // node_module 7 | import App from './App.vue' // 解析成额外的 ?type=template请求 8 | import './index.css' 9 | 10 | createApp(App).mount('#app') 11 | 12 | 13 | -------------------------------------------------------------------------------- /mini-vue-plus/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /mini-vue-plus/App.js: -------------------------------------------------------------------------------- 1 | // const { effect, reactive } = require("@vue/reactivity"); 2 | // const { effectWatch, reactive } = require("./core/reactivity"); 3 | import { effectWatch, reactive } from "./core/reactivity/index.js"; 4 | import { h } from "./core/h.js"; 5 | 6 | // v1 7 | // let a = 10; 8 | // let b = a + 10; 9 | // console.log(b) 10 | 11 | // a = 20; 12 | // b = a + 10; 13 | // console.log(b) 14 | 15 | // v2 16 | 17 | // let a = 10; 18 | // let b; 19 | // update(); 20 | // function update() { 21 | // b = a + 10; 22 | // console.log(b); 23 | // } 24 | 25 | // a = 20; 26 | // update(); 27 | 28 | // v3 29 | // a 发生变更了 ,我想让 b 自动更新 30 | 31 | // 声明一个响应式对象 32 | let a = reactive({ 33 | value: 1, 34 | }); 35 | let b; 36 | effectWatch(() => { 37 | // 函数 38 | // 1. 会执行以下 39 | b = a.value + 10; 40 | console.log(b); 41 | }); 42 | 43 | // a 响应式对象的值发生改变之后 44 | a.value = 30; 45 | 46 | // vue3 47 | 48 | export default { 49 | // template -> render 50 | render(context) { 51 | // 构建 view = b 52 | // view -> 每次我都需要重新的创建 53 | // 要计算出最小的更新点 -> vdom 54 | // js ——> diff 55 | // reset 56 | // tag 57 | // props 58 | // children 59 | // const div = document.createElement("div"); 60 | // div.innerText = context.state.count; 61 | // return div; 62 | return h( 63 | "div", 64 | { 65 | id: "app - " + context.state.count, 66 | class: "showTim", 67 | }, 68 | // String(context.state.count) 69 | [h("p", null, String(context.state.count)), h("p", null, "hahha")] 70 | ); 71 | }, 72 | setup() { 73 | // a = 响应式数据 74 | const state = reactive({ 75 | count: 0, 76 | }); 77 | window.state = state; 78 | return { state }; 79 | }, 80 | }; 81 | -------------------------------------------------------------------------------- /mini-vue-plus/core/h.js: -------------------------------------------------------------------------------- 1 | // 创建一个虚拟节点 vdom vnode 2 | export function h(tag, props, children) { 3 | return { 4 | tag, 5 | props, 6 | children, 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /mini-vue-plus/core/index.js: -------------------------------------------------------------------------------- 1 | import { effectWatch } from "./reactivity/index.js"; 2 | import { mountElement,diff } from "./renderer/index.js"; 3 | export function createApp(rootComponent) { 4 | return { 5 | mount(rootContainer) { 6 | const context = rootComponent.setup(); 7 | let isMounted = false; 8 | let prevSubTree; 9 | 10 | effectWatch(() => { 11 | if (!isMounted) { 12 | // init 13 | isMounted = true 14 | rootContainer.innerHTML = ``; 15 | const subTree = rootComponent.render(context); 16 | console.log(subTree); 17 | mountElement(subTree, rootContainer); 18 | prevSubTree = subTree; 19 | 20 | } else { 21 | // update 22 | const subTree = rootComponent.render(context); 23 | diff(prevSubTree, subTree); 24 | prevSubTree = subTree; 25 | } 26 | 27 | // diff 28 | // newVnode oldVnode 29 | // rootContainer.append(element); 30 | }); 31 | }, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /mini-vue-plus/core/reactivity/index.js: -------------------------------------------------------------------------------- 1 | // 响应式库 2 | 3 | // 依赖 4 | let currentEffect; 5 | class Dep { 6 | constructor(val) { 7 | // es6+ 8 | this.effects = new Set(); 9 | this._val = val; 10 | } 11 | 12 | get value() { 13 | this.depend(); 14 | return this._val; 15 | } 16 | set value(newVal) { 17 | this._val = newVal; 18 | this.notice(); 19 | } 20 | 21 | // 1. 收集依赖 22 | depend() { 23 | if (currentEffect) { 24 | this.effects.add(currentEffect); 25 | } 26 | } 27 | 28 | // 2. 触发依赖 29 | notice() { 30 | // 触发一下我们之前收集到的依赖 31 | this.effects.forEach((effect) => { 32 | effect(); 33 | }); 34 | } 35 | } 36 | 37 | export function effectWatch(effect) { 38 | // 收集依赖 39 | currentEffect = effect; 40 | effect(); 41 | currentEffect = null; 42 | } 43 | 44 | // ref -> 很像了 45 | // const dep = new Dep(10); 46 | 47 | // let b; 48 | 49 | // effectWatch(() => { 50 | // b = dep.value + 10; 51 | // console.log(b); 52 | // }); 53 | 54 | // // 值发生变更 55 | // dep.value = 20; 56 | 57 | // reactive 58 | // dep -> number string 59 | // object -> key -> dep 60 | 61 | // 1. 这个对象在什么时候改变的 62 | // object.a -> get 63 | // object.a = 2 -> set 64 | 65 | // vue2 66 | // proxy 67 | const targetMap = new Map(); 68 | 69 | function getDep(target, key) { 70 | let depsMap = targetMap.get(target); 71 | if (!depsMap) { 72 | depsMap = new Map(); 73 | targetMap.set(target, depsMap); 74 | } 75 | 76 | let dep = depsMap.get(key); 77 | if (!dep) { 78 | dep = new Dep(); 79 | depsMap.set(key, dep); 80 | } 81 | 82 | return dep; 83 | } 84 | 85 | export function reactive(raw) { 86 | return new Proxy(raw, { 87 | get(target, key) { 88 | // key - dep 89 | // dep 我们存储在哪里 90 | const dep = getDep(target, key); 91 | 92 | // 依赖收集 93 | dep.depend(); 94 | 95 | return Reflect.get(target, key); 96 | }, 97 | set(target, key, value) { 98 | // / 触发依赖 99 | // 要获取到 de 100 | const dep = getDep(target, key); 101 | const result = Reflect.set(target, key, value); 102 | dep.notice(); 103 | return result; 104 | }, 105 | }); 106 | } 107 | 108 | // const user = reactive({ 109 | // age: 19, 110 | // }); 111 | 112 | // let double; 113 | // effectWatch(() => { 114 | // console.log("---reactive--"); 115 | // double = user.age; 116 | // console.log(double); 117 | // }); 118 | 119 | // user.age = 20; 120 | -------------------------------------------------------------------------------- /mini-vue-plus/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mini-vue-plus/index.js: -------------------------------------------------------------------------------- 1 | import App from "./App.js"; 2 | import { createApp } from "./core/index.js"; 3 | 4 | createApp(App).mount(document.querySelector("#app")); 5 | -------------------------------------------------------------------------------- /mini-vue-plus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "up-mini-vue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@vue/reactivity": "^3.0.5" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mini-vue/00_original.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
8 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /mini-vue/01_mvvm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /mini-vue/02_reactivity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /mini-vue/03-render.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /mini-vue/compiler/simple.js: -------------------------------------------------------------------------------- 1 | let startTag = /<([a-zA-Z_][\w\-\.]*)((?:\s+([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))*)\s*(\/?)>/; 2 | // // < 任意内容 任意内容="" > 3 | let endTag = /<\/([a-zA-Z_][\w\-\.]*)>/; // 4 | 5 | let attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g; 6 | const bufArray = []; 7 | function parser(html) { 8 | while (html && last !== html) { 9 | if (html.indexOf("hij`; 22 | // const ast = parser(html); 23 | // console.log("ast", ast); 24 | // console.log(html.match(startTag)) 25 | 26 | // var str = "abcdbce"; 27 | // str = str.replace(/(b)(c)/g, (...args) => { 28 | // console.log(args); 29 | // return "&&"; 30 | // }); 31 | // console.log(str); 32 | 33 | // startTag = /<([a-zA-Z_][\w\-\.]*)>/g 34 | // ([a-zA-Z_][\w\-\.]*) 标签头部 包含下划线和. 35 | // <字母>
<([a-zA-Z]*)> 36 | // <字母 字母=字母> <([a-zA-Z]*)(\s*([a-zA-Z]*))=> 37 | // <字母 字母=字母> 可以加空格 \s* 38 | // startTag = /<([a-zA-Z]*)(\s*([a-zA-Z]*))=("*[a-zA-Z]*"*)\s*>/g 39 | // 多attr 40 | startTag = /<([a-z]*)(((\s+([a-z0-9]*))=("[^"]*"))*)\s*>/g; 41 | // startTag = /<([a-zA-Z_]*)((\s+([a-zA-Z][-a-zA-Z0-9_:.]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))*)\s*>/ 42 | 43 | // attr = /([a-zA-Z0-9)=("[^"]*")/g; 44 | attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g; 45 | 46 | // 找到标签头部 47 | match = html.match(startTag); 48 | console.log("找到头部内容", match); 49 | if (match) { 50 | chars = false; 51 | html = html.substring(match[0].length); 52 | 53 | console.log("html", html); 54 | // 递归调用 55 | match[0].replace(startTag, parseStartTag); 56 | console.log("============End============"); 57 | console.log(match[0], bufArray); 58 | } 59 | 60 | function parseStartTag(tag, tagName, rest) { 61 | console.log("parseTag头部:", arguments); 62 | tagName = tagName.toLowerCase(); 63 | 64 | // 解析属性 65 | const attrs = []; 66 | let unary = !!arguments[7]; 67 | 68 | const node = { 69 | node: "element", 70 | tag: tagName, 71 | }; 72 | 73 | // 解析属性 74 | console.log("rest", rest); 75 | rest.replace(attr, function (match, name, value) { 76 | console.log("attr replace:",arguments); 77 | 78 | // const value = arguments[2] 79 | // ? arguments[2] 80 | // : arguments[3] 81 | // ? arguments[3] 82 | // : arguments[4] 83 | // ? arguments[4] 84 | // : ""; 85 | 86 | // console.log("===", arguments[2]); 87 | 88 | // const value = arguments[2] ? arguments[2] : arguments[3]; 89 | 90 | console.log("value", value); 91 | 92 | if (name == "class") { 93 | node.class = value; 94 | } else { 95 | attrs.push({ 96 | name, 97 | value, 98 | }); 99 | } 100 | }); 101 | // node.dataset = ds; 102 | node.attrs = attrs; 103 | if (!unary) { 104 | bufArray.push(node); 105 | } else { 106 | pushChild(node); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /mini-vue/complete/App.js: -------------------------------------------------------------------------------- 1 | import { reactive } from "./core/reactivity/index.js"; 2 | import { h } from "./core/h.js"; 3 | 4 | export const App = { 5 | render(content) { 6 | return h("div", { testId: content.state.testId }, [ 7 | h("div", null, content.state.title), 8 | h("div", null, String(content.state.count)), 9 | h( 10 | "button", 11 | { 12 | onClick: content.state.onClick, 13 | }, 14 | "click" 15 | ), 16 | ]); 17 | }, 18 | 19 | setup() { 20 | const state = reactive({ 21 | title: "mini-vue", 22 | count: 0, 23 | testId: "heihei", 24 | onClick() { 25 | state.testId = "hahah"; 26 | state.count++; 27 | console.log(state); 28 | }, 29 | }); 30 | 31 | window.state1 = reactive({ 32 | content: "hello", 33 | }); 34 | 35 | window.state2 = reactive({ 36 | content: [h("p", null, "123")], 37 | }); 38 | 39 | return { 40 | state, 41 | state1, 42 | state2, 43 | }; 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /mini-vue/complete/core/h.js: -------------------------------------------------------------------------------- 1 | export function h(tag, props, children = []) { 2 | return { 3 | tag, 4 | props, 5 | children, 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /mini-vue/complete/core/index.js: -------------------------------------------------------------------------------- 1 | // 入口 2 | import { watchEffect } from "./reactivity/index.js"; 3 | import { mountElement, diff } from "./renderer.js"; 4 | 5 | export function createApp(rootComponent) { 6 | const app = { 7 | mount(rootContainer) { 8 | const setupResult = rootComponent.setup(); 9 | let isMounted = false; 10 | let prevSubTree; 11 | watchEffect(() => { 12 | if (!isMounted) { 13 | // mount 14 | isMounted = true; 15 | const subTree = rootComponent.render(setupResult); 16 | prevSubTree = subTree; 17 | mountElement(subTree, rootContainer); 18 | } else { 19 | // update 20 | const subTree = rootComponent.render(setupResult); 21 | diff(prevSubTree, subTree); 22 | prevSubTree = subTree; 23 | } 24 | }); 25 | }, 26 | }; 27 | return app; 28 | } 29 | -------------------------------------------------------------------------------- /mini-vue/complete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | mini-vue 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /mini-vue/complete/main.js: -------------------------------------------------------------------------------- 1 | import { App } from "./App.js"; 2 | import { createApp } from "./core/index.js"; 3 | 4 | 5 | createApp(App).mount(document.querySelector("#app")); 6 | -------------------------------------------------------------------------------- /mini-vue/options_api/00_original.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
8 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /mini-vue/options_api/01_mvvm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /mini-vue/options_api/02_reactivity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /mini-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-vue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /mini-vue/reactivity/__test__/index.spec.js: -------------------------------------------------------------------------------- 1 | const { reactive, watchEffect } = require('../index') 2 | 3 | describe('reactivity/vue3', () => { 4 | 5 | it('测试数据改变时 是否被响应', () => { 6 | const data = reactive({ 7 | name: 'abc', 8 | age: { 9 | n: 5 10 | } 11 | }) 12 | // Mock一个响应函数 13 | const fn = jest.fn() 14 | 15 | // 设置响应函数 16 | watchEffect(fn) 17 | // 改变数据 18 | data.name = 'efg' 19 | // 确认fn生效 20 | expect(fn).toBeCalled() 21 | }) 22 | 23 | 24 | it('订阅发布 ', () => { 25 | const data = reactive({ 26 | age: { 27 | n: 5 28 | } 29 | }) 30 | // Mock一个响应函数 31 | const fn1 = jest.fn() 32 | const fn2 = jest.fn() 33 | // 设置响应函数 34 | watchEffect(fn1) 35 | watchEffect(fn2) 36 | // 改变多层数据 37 | data.age.n = 1 38 | // 确认fn生效 39 | expect(fn1).toBeCalled() 40 | expect(fn2).toBeCalled() 41 | }) 42 | 43 | 44 | it('测试多层数据中改变时 是否被响应', () => { 45 | const data = reactive({ 46 | age: { 47 | n: 5 48 | } 49 | }) 50 | // Mock一个响应函数 51 | const fn = jest.fn() 52 | // 设置响应函数 53 | watchEffect(fn) 54 | // 改变多层数据 55 | data.age.n = 1 56 | // 确认fn生效 57 | expect(fn).toBeCalled() 58 | }) 59 | 60 | 61 | it('测试数组中数据改变时 是否被响应', () => { 62 | const data = reactive({ 63 | ary: [ 64 | 'a' 65 | ] 66 | }) 67 | // Mock一个响应函数 68 | const fn = jest.fn() 69 | 70 | // 设置响应函数 71 | watchEffect(fn) 72 | 73 | // 改变多层数据 74 | data.ary.push('b') 75 | 76 | // 确认fn生效 77 | expect(fn).toBeCalled() 78 | }) 79 | }) -------------------------------------------------------------------------------- /mini-vue/runtime/diff/__tests__/index.spec.js: -------------------------------------------------------------------------------- 1 | exports.diffArray = (n1,n2, { 2 | mountElement, 3 | remove 4 | }) => { 5 | mountElement(1) 6 | 7 | remove(5) 8 | } -------------------------------------------------------------------------------- /mini-vue/runtime/diff/index.js: -------------------------------------------------------------------------------- 1 | exports.diffArray = (n1,n2, { 2 | mountElement, 3 | remove 4 | }) => { 5 | mountElement(1) 6 | 7 | remove(5) 8 | } -------------------------------------------------------------------------------- /mini-vue/runtime/h.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 返回一个虚拟DOM节点 3 | * @param {*} tag 4 | * @param {*} props 5 | * @param {*} children 6 | */ 7 | function h(tag, props, children = []) { 8 | return { 9 | tag, 10 | props, 11 | children, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /mini-vuepress/README.md: -------------------------------------------------------------------------------- 1 | # Markdown 666 2 | 3 | ```bash 4 | yarn add vue@3.0.0-beta.10 5 | ``` 6 | # CSS样式主题库 7 | https://www.sohu.com/a/312891335_100298843 8 | -------------------------------------------------------------------------------- /mini-vuepress/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const Koa = require('koa') 4 | const { tranHtml } = require('./markdown') 5 | const app = new Koa() 6 | const ssr = require('./ssr') 7 | const glob = require('glob') 8 | const io = require('socket.io') 9 | const server = require('http').createServer(app.callback()); 10 | const watch = require('watch') 11 | const { nextTick } = require('process') 12 | var socket = io.listen(server); 13 | const clients = [] 14 | socket.on('connection', function (client) { 15 | console.log('网页监听....') 16 | clients.push(client) 17 | }); 18 | watch.watchTree('.', function (f, curr, prev) { 19 | socket.emit('reload',f) 20 | console.log('file change ...') 21 | }) 22 | 23 | 24 | function getFolder() { 25 | return glob.sync( 26 | require('path').join(__dirname, `./**/*.md`), 27 | { 28 | absolute: false, 29 | } 30 | ) 31 | .map( 32 | v => path.relative(__dirname, v) 33 | ) 34 | .filter(v => !v.startsWith('node_modules')) 35 | } 36 | 37 | 38 | 39 | 40 | app.use(async (ctx, next) => { 41 | const { request: { url, query } } = ctx 42 | console.log('url:' + url, 'query type', query.type) 43 | 44 | ctx.type = "text/html" 45 | const menu = getFolder() 46 | const markDownPath = url === '/' ? './README.md' : url 47 | console.log('markDownPath:', markDownPath) 48 | const data = { 49 | menu, 50 | markdown: tranHtml(path.resolve(__dirname, './' + markDownPath)) 51 | } 52 | ctx.body = await ssr.createRender(path.resolve(__dirname, './template/App.vue'))(data) 53 | next() 54 | }) 55 | 56 | app.use(async ctx => { 57 | ctx.body = ` 58 | 59 | 60 | 61 | ${ctx.body} 62 | 63 | 72 | 73 | 74 | 75 | ` 76 | 77 | }) 78 | 79 | server.listen(3000, () => { 80 | console.log('listening on *:3000'); 81 | }); 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /mini-vuepress/markdown/__tests__/index.spec.js: -------------------------------------------------------------------------------- 1 | it('markdown transfrer', () => { 2 | const { tranHtml } = require('../index') 3 | 4 | // expect(tranHtml(__dirname + '/test.md', __dirname + '/test.css')).toBe( 5 | // `

T1

6 | // 9 | // `) 10 | 11 | }) -------------------------------------------------------------------------------- /mini-vuepress/markdown/__tests__/test.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin: 0 auto; 3 | } -------------------------------------------------------------------------------- /mini-vuepress/markdown/__tests__/test.md: -------------------------------------------------------------------------------- 1 | # T1 -------------------------------------------------------------------------------- /mini-vuepress/markdown/index.js: -------------------------------------------------------------------------------- 1 | const marked = require('marked') 2 | const fs = require('fs') 3 | module.exports.tranHtml = (markdown, style = __dirname + '/mark.css') => { 4 | return `${marked(fs.readFileSync(markdown).toString())} 5 | 6 | ` 7 | } -------------------------------------------------------------------------------- /mini-vuepress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-vite", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@vue/compiler-dom": "^3.0.0-rc.5", 14 | "@vue/compiler-sfc": "^3.0.7", 15 | "@vue/server-renderer": "^3.0.0-rc.4", 16 | "glob": "^7.1.6", 17 | "koa": "^2.12.0", 18 | "marked": "^1.1.1", 19 | "socket.io": "^2.3.0", 20 | "vue": "^3.0.0-rc.5", 21 | "watch": "^1.0.2" 22 | }, 23 | "devDependencies": { 24 | "express": "^4.17.1", 25 | "vite": "^0.20.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mini-vuepress/scaner.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/mini-vuepress/scaner.js -------------------------------------------------------------------------------- /mini-vuepress/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /mini-vuepress/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/mini-vuepress/src/assets/logo.png -------------------------------------------------------------------------------- /mini-vuepress/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /mini-vuepress/src/index.css: -------------------------------------------------------------------------------- 1 | h1{ 2 | color:red; 3 | } 4 | -------------------------------------------------------------------------------- /mini-vuepress/src/main.js: -------------------------------------------------------------------------------- 1 | // import 都会发起一个网络请求 viet拦截这个请求,渲染 2 | import { createApp } from 'vue' // node_module 3 | import App from './App.vue' // 解析成额外的 ?type=template请求 4 | import './index.css' 5 | 6 | createApp(App).mount('#app') 7 | 8 | // alert('2') 9 | -------------------------------------------------------------------------------- /mini-vuepress/ssr/App.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /mini-vuepress/ssr/__tests__/index.spec.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('@vue/compiler-dom') 2 | const path = require('path') 3 | it('sfc ssr render', async () => { 4 | const { createRender } = require('../index') 5 | const render = createRender(path.resolve(__dirname,'../App.vue')) 6 | const data = { 7 | todos: ['吃饭', '睡觉'] 8 | } 9 | const html = await render(data) 10 | expect(html).toBe('
') 11 | }) -------------------------------------------------------------------------------- /mini-vuepress/ssr/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 |
5 | 39 | -------------------------------------------------------------------------------- /mini-vuepress/ssr/index.js: -------------------------------------------------------------------------------- 1 | const Vue = require('vue') // vue@next 2 | const serverRenderer = require('@vue/server-renderer') 3 | const compilerSsr = require('@vue/compiler-ssr') 4 | const compilerSfc = require('@vue/compiler-sfc') 5 | const fs = require('fs') 6 | 7 | module.exports.createRender = path => { 8 | const { descriptor } = compilerSfc.parse(fs.readFileSync(path, 'utf-8')) 9 | const render = compilerSsr.compile(descriptor.template.content).code 10 | 11 | return async (data) => { 12 | const app = Vue.createApp({ 13 | ssrRender: new Function('require',render)(require), // 写法二 14 | data: () => data 15 | }) 16 | return serverRenderer.renderToString(app) 17 | } 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /mini-vuepress/ssr/list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | template: `
5 |
6 |
    7 |
  • kkb
  • 8 |
  • kkb
  • 9 |
  • kkb
  • 10 |
  • kkb
  • 11 |
  • kkb
  • 12 |
  • kkb
  • 13 |
  • {{n}}--{{todo}}
  • 14 |
15 |
16 |
`, 17 | data(){ 18 | return { 19 | todos:['吃饭','睡觉'] 20 | } 21 | } 22 | } 23 | 24 | 25 | // const self = (global || root) 26 | 27 | // self.performance = { 28 | // now: function () { 29 | // var hrtime = process.hrtime() 30 | // return ((hrtime[0] * 1000000 + hrtime[1] / 1000) / 1000) 31 | // } 32 | // } 33 | 34 | // function generateGrid (rowCount, columnCount) { 35 | // var grid = [] 36 | 37 | // for (var r = 0; r < rowCount; r++) { 38 | // var row = { id: r, items: [] } 39 | // for (var c = 0; c < columnCount; c++) { 40 | // row.items.push({ id: (r + '-' + c) }) 41 | // } 42 | // grid.push(row) 43 | // } 44 | 45 | // return grid 46 | // } 47 | 48 | // const gridData = generateGrid(100, 5) 49 | 50 | // module.exports = { 51 | // template: '

{{ Math.random() }}

', 52 | // components: { 53 | // myTable: { 54 | // data: function () { 55 | // return { 56 | // grid: gridData 57 | // } 58 | // }, 59 | // // template: '
123{{ item.id }}
', 60 | // template: '
', 61 | // components: { 62 | // row: { 63 | // props: ['row'], 64 | // template: '{{ Math.random() }}', 65 | // components: { 66 | // column: { 67 | // template: '' + 68 | // // 25 plain elements for each cell 69 | // '' + 74 | // '' 75 | // } 76 | // } 77 | // } 78 | // } 79 | // } 80 | // } 81 | // } 82 | -------------------------------------------------------------------------------- /mini-vuepress/ssr/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express() 3 | const vueapp = require('./list') 4 | const Vue = require('vue') // vue@next 5 | const serverRenderer = require('@vue/server-renderer') 6 | const compilerSsr = require('@vue/compiler-ssr') 7 | const compilerSfc = require('@vue/compiler-sfc') 8 | const fs = require('fs') 9 | vueapp.ssrRender = new Function('require', compilerSsr.compile(vueapp.template).code)(require) 10 | 11 | app.get('/', async function (req, res) { 12 | const { descriptor } = compilerSfc.parse(fs.readFileSync('./App.vue', 'utf-8')) 13 | console.log(descriptor.template.content) 14 | 15 | const data = () => ({ 16 | todos: ['吃饭', '睡觉'] 17 | }) 18 | 19 | const render = compilerSsr.compile(descriptor.template.content).code 20 | 21 | let vapp = Vue.createApp({ 22 | // template: descriptor.template.content, // 写法一 23 | ssrRender: new Function('require',render)(require), // 写法二 24 | data 25 | }) 26 | let html = await serverRenderer.renderToString(vapp) 27 | 28 | res.send(html) 29 | }) 30 | 31 | app.listen(9093, () => { 32 | console.log('listen 9093') 33 | }) 34 | -------------------------------------------------------------------------------- /mini-vuepress/template/App.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /mini-vuepress/vite/README.md: -------------------------------------------------------------------------------- 1 | # Vite 666 -------------------------------------------------------------------------------- /mini-vuepress/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |

kkb

11 |
12 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /mini-vuepress/vite/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const Koa = require('koa') 4 | const compilerSfc = require('@vue/compiler-sfc') // .vue 5 | const compilerDom = require('@vue/compiler-dom') // 模板 6 | const {tranHtml} = require('./markdown') 7 | const app = new Koa() 8 | function rewriteImport(content) { 9 | return content.replace(/from ['|"]([^'"]+)['|"]/g, function (s0, s1) { 10 | // . ../ /开头的,都是相对路径 11 | if (s1[0] !== '.' && s1[1] !== '/') { 12 | return `from '/@modules/${s1}'` 13 | } else { 14 | return s0 15 | } 16 | }) 17 | } 18 | 19 | app.use(async ctx => { 20 | const { request: { url, query } } = ctx 21 | console.log('url:' + url,'query type',query.type) 22 | // 首页 23 | if (url == '/') { 24 | ctx.type = "text/html" 25 | let content = fs.readFileSync('./index.html', 'utf-8') 26 | content = content.replace(' 30 | 5 |
6 | Counter: {{ counter }} 7 | 8 |
9 | 10 | 20 | -------------------------------------------------------------------------------- /petitevue/counter/options.html: -------------------------------------------------------------------------------- 1 | 5 |
6 | Counter: {{ counter }} 7 | 8 |
9 | 19 | -------------------------------------------------------------------------------- /petitevue/counter/petite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | {{ count }} 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /petitevue/counter/vuex.html: -------------------------------------------------------------------------------- 1 | 2 | 18 |
19 | 20 | {{ store.count }} 21 |
22 |
23 |
24 |
25 |
26 | 27 | {{ store.count }}: {{c2}} 28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /scalingup/router/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 |
12 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /scalingup/ssr/App.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /scalingup/ssr/index.js: -------------------------------------------------------------------------------- 1 | const Vue = require("vue"); // vue@next 2 | const serverRenderer = require("@vue/server-renderer"); 3 | const compilerSsr = require("@vue/compiler-ssr"); 4 | const compilerSfc = require("@vue/compiler-sfc"); 5 | const fs = require("fs"); 6 | const path = require("path"); 7 | 8 | const createRender = (path) => { 9 | const { descriptor } = compilerSfc.parse(fs.readFileSync(path, "utf-8")); 10 | const render = compilerSsr.compile(descriptor.template.content).code; 11 | return async (data) => { 12 | const app = Vue.createApp({ 13 | ssrRender: new Function("require", render)(require), // 写法二 14 | data: () => data, 15 | }); 16 | return serverRenderer.renderToString(app); 17 | }; 18 | }; 19 | 20 | const render = createRender(path.resolve(__dirname, "./App.vue")); 21 | const data = { 22 | todos: ["吃饭", "睡觉"], 23 | }; 24 | render(data).then((html) => { 25 | console.log("html:", html); 26 | }); 27 | -------------------------------------------------------------------------------- /scalingup/ssr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ssr", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@vue/compiler-dom": "^3.0.7", 14 | "@vue/compiler-sfc": "^3.0.7", 15 | "@vue/compiler-ssr": "^3.0.7", 16 | "@vue/server-renderer": "^3.0.7", 17 | "vue": "^3.0.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scalingup/state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 14 | 15 |
{{sharedState.message}}
16 |
{{sharedState.message}}
17 | 18 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /toolchain/babel/index.js: -------------------------------------------------------------------------------- 1 | // console.log 和 console.error 中插入代码的位置信息 2 | //一些参数的功能。 3 | const parser = require('@babel/parser') 4 | const traverse = require('@babel/traverse').default 5 | const generator = require('@babel/generator').default 6 | const types = require('@babel/types') 7 | 8 | const sourceFileName = 'sourceFile.js' 9 | 10 | const sourceCode = ` 11 | console.log(1) 12 | function log() : number { 13 | console.debug('before'); 14 | console.error(2); 15 | console.debug('after'); 16 | } 17 | log(); 18 | class Foo { 19 | bar(): void { 20 | console.log(3) 21 | } 22 | render() { 23 | return '' 24 | // return
25 | } 26 | } 27 | ` 28 | 29 | const ast = parser.parse(sourceCode, { 30 | plugins: ['typescript','jsx'] 31 | }) 32 | 33 | traverse(ast, { 34 | CallExpression(path) { 35 | const calleeStr = generator(path.node.callee).code 36 | if(['console.log','console.error'].includes(calleeStr)) { 37 | const {line,column} = path.node.loc.start 38 | path.node.arguments.unshift(types.stringLiteral(`${sourceFileName}(${line},${column}):`)) 39 | 40 | } 41 | } 42 | }) 43 | 44 | const {code ,map } = generator(ast, { 45 | sourceMaps: true, 46 | sourceFileName 47 | }) 48 | console.log('code ',code) 49 | 50 | console.log('map',map) -------------------------------------------------------------------------------- /toolchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toolchain", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@babel/parser": "^7.14.3", 14 | "@babel/traverse": "^7.14.2", 15 | "@babel/types": "^7.14.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /toolchain/yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /usr/local/bin/node /usr/local/bin/yarn add @babel/core @babel/template @babel/types @babel/generate @babel/traverse @babel/parser 3 | 4 | PATH: 5 | /Users/xiaran/opt/anaconda3/bin:/Users/xiaran/opt/anaconda3/condabin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 6 | 7 | Yarn version: 8 | 1.22.4 9 | 10 | Node version: 11 | 14.7.0 12 | 13 | Platform: 14 | darwin x64 15 | 16 | Trace: 17 | Error: https://registry.npm.taobao.org/@babel%2fgenerate: Not found 18 | at Request.params.callback [as _callback] (/usr/local/lib/node_modules/yarn/lib/cli.js:66987:18) 19 | at Request.self.callback (/usr/local/lib/node_modules/yarn/lib/cli.js:140748:22) 20 | at Request.emit (events.js:314:20) 21 | at Request. (/usr/local/lib/node_modules/yarn/lib/cli.js:141720:10) 22 | at Request.emit (events.js:314:20) 23 | at IncomingMessage. (/usr/local/lib/node_modules/yarn/lib/cli.js:141642:12) 24 | at Object.onceWrapper (events.js:420:28) 25 | at IncomingMessage.emit (events.js:326:22) 26 | at endReadableNT (_stream_readable.js:1244:12) 27 | at processTicksAndRejections (internal/process/task_queues.js:80:21) 28 | 29 | npm manifest: 30 | { 31 | "name": "toolchain", 32 | "version": "1.0.0", 33 | "description": "", 34 | "main": "index.js", 35 | "scripts": { 36 | "test": "echo \"Error: no test specified\" && exit 1" 37 | }, 38 | "keywords": [], 39 | "author": "", 40 | "license": "ISC" 41 | } 42 | 43 | yarn manifest: 44 | No manifest 45 | 46 | Lockfile: 47 | No lockfile 48 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | *.local -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/README.assets/image-20201206165135473.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/README.assets/image-20201206165135473.png -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/README.assets/image-20201206171613601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/README.assets/image-20201206171613601.png -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/README.assets/image-20201206172905686.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/README.assets/image-20201206172905686.png -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/README.assets/image-20201206174225781.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/README.assets/image-20201206174225781.png -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "dependencies": { 9 | "vue": "^3.0.0-rc.1" 10 | }, 11 | "devDependencies": { 12 | "vite": "^1.0.0-rc.1", 13 | "@vue/compiler-sfc": "^3.0.0-rc.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/public/favicon.ico -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 53 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/assets/flower.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/src/assets/flower.webm -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/composition-api/demo/src/assets/logo.png -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Computed.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/LifecycleHooks.js: -------------------------------------------------------------------------------- 1 | import { onBeforeMount,onMounted } from "vue"; 2 | export default { 3 | setup() { 4 | onBeforeMount(() => { 5 | console.log('Before Mount!') 6 | }) 7 | onMounted(() => { 8 | console.log('Before Mount!') 9 | }) 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Method.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Modularizing/Modularzing.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Modularizing/index.js: -------------------------------------------------------------------------------- 1 | import Modularzing from './Modularzing.vue' 2 | export default Modularzing -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Setup.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 31 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/SharingState/Origin.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/SharingState/SharingState.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/SharingState/index.js: -------------------------------------------------------------------------------- 1 | import SharingState from './SharingState.vue' 2 | export default SharingState -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/SharingState/usePromise.js: -------------------------------------------------------------------------------- 1 | import { ref } from "vue"; 2 | 3 | export default function usePromise(fn) { 4 | const results = ref(null); 5 | // is PENDING 6 | const loading = ref(false); 7 | const error = ref(null); 8 | 9 | const createPromise = async (...args) => { 10 | loading.value = true; 11 | error.value = null; 12 | results.value = null; 13 | try { 14 | results.value = await fn(...args); 15 | } catch (err) { 16 | error.value = err; 17 | } finally { 18 | loading.value = false; 19 | } 20 | }; 21 | return { results, loading, error, createPromise }; 22 | } 23 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Suspense/Event.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Suspense/Suspense.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Suspense/index.js: -------------------------------------------------------------------------------- 1 | import Suspense from './Suspense.vue' 2 | export default Suspense -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Teleport.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/Watch.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 39 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/composables/use-event-space.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from "vue"; 2 | export default function useEventSpace() { 3 | const capacity = ref(3); 4 | const attending = ref(["Tim", "Bob", "Joe"]); 5 | function increaseCapacity() { 6 | capacity.value++; 7 | } 8 | const sapcesLeft = computed(() => { 9 | return capacity.value - attending.value.length; 10 | }); 11 | 12 | return { capacity, attending, increaseCapacity, sapcesLeft }; 13 | } 14 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/components/reactiveSynatx.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/index.css: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: Avenir, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | text-align: center; 6 | color: #2c3e50; 7 | margin-top: 60px; 8 | } 9 | -------------------------------------------------------------------------------- /vue-mastery/composition-api/demo/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import './index.css' 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/deep-dive/README.md -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/03/render-fn.js: -------------------------------------------------------------------------------- 1 | import { h } from "vue"; 2 | 3 | export default { 4 | render() { 5 | // return h("div", { 6 | // id:'hello' 7 | // }, [ 8 | // h('span','world') 9 | // ]); 10 | 11 | // v-if 12 | // let nodeToReturn 13 | // if(this.ok) { 14 | // // nodeToReturn = ... 15 | // }else if() { 16 | 17 | // } 18 | 19 | // return this.ok 20 | // ? h( 21 | // "div", 22 | // { 23 | // id: "hello", 24 | // }, 25 | // [h("span", "world")] 26 | // ) 27 | // : this.otherCondition 28 | // ? h("p", "other branch") 29 | // : h("span") 30 | 31 | // v-for="item in list" 32 | // return this.list.map((item) => { 33 | // return h( 34 | // "div", 35 | // { 36 | // key: item.id, 37 | // }, 38 | // item.text 39 | // ); 40 | // }); 41 | 42 | // slots 43 | // default是渲染函数 44 | const slot = this.$slots.default ? this.$slots.default({}) : []; 45 | 46 | slot.map((vnode) => { 47 | return h("div", [vnode]); 48 | }); 49 | 50 | return h( 51 | "div", 52 | { class: "stack" }, 53 | slot.map((child) => { 54 | return h("div", { class: `mt-${this.$props.size}` }, [child]); 55 | }) 56 | ); 57 | }, 58 | }; 59 | //
world
60 | 61 | { 62 | /* 63 |
hello
64 | 65 |
hello
66 |
hello
67 |
68 |
; 69 | 70 |
71 |
72 |
hello
73 |
74 |
75 |
76 |
77 |
hello
78 |
79 |
80 |
81 |
; */ 82 | } 83 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/03/stack.html: -------------------------------------------------------------------------------- 1 | 2 | 7 |
8 | 9 | 45 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/04/render-fn.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vue-mastery/deep-dive/demo/04/render-fn.js -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/04/vdom.html: -------------------------------------------------------------------------------- 1 | 6 |
7 | 79 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/ScopeSlots.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/compositionAPI.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/higherOrderCompent.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/mouse.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/reusable.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/13/vue-mastery-DiveP13.md: -------------------------------------------------------------------------------- 1 | 为什么vue3.0很好的解决逻辑复用问题 2 | 3 | vue2.0 是如何解决逻辑重用问题的? 4 | 5 | 举例子 6 | 7 | 封装一个组件,跟踪鼠标的位置,在页面中打印出来, 8 | 9 | 在项目中封装好这个组件,可能会需要跨组件重用他 10 | 11 | image-20210103190712089 12 | 13 | 如果他不是根组件的话,可能会被销毁 14 | 15 | 当有一个以及以上的mixin的话,我们可以看到x,y是来自于mixin, 16 | 17 | 如果名字更混乱,并且你有2个3个mixin,每次注入不同的名字 18 | 19 | 你会搞不清楚哪个变量来自于哪个mixin 20 | 21 | 另一个问题是你不得不担心命名冲突,就是2个mixin里面都有同样的函数,你需要 重命名 22 | 23 | react 完全移除了mixin,他们没有一个很好的替代品,他们的解决方案是一种叫做高阶组件的东西 24 | 25 | 是通过props,你需要什么就注入什么 26 | 27 | 将所需要的数据x,y注入其中 28 | 29 | 不能真正的解决问题 30 | 31 | -------------------------------------------------------------------------------- /vue-mastery/deep-dive/demo/reactivity.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vuex/01-introduction/cdn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/01-state.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/02-getter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/03-mutation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/05-module/01-basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/05-module/02-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /vuex/02-core-concepts/05-module/03-register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /vuex/03-advanced/02-composition-api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /vuex/03-advanced/03-plugins/01-helloworld.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /vuex/03-advanced/03-plugins/02-websocket/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Document 13 | 14 | 15 |
16 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /vuex/03-advanced/03-plugins/02-websocket/index.js: -------------------------------------------------------------------------------- 1 | var app = require("express")(); 2 | var http = require("http").Server(app); 3 | var io = require("socket.io")(http); 4 | 5 | let i = 0; 6 | app.get("/", function (req, res) { 7 | res.sendFile(__dirname + "/index.html"); 8 | }); 9 | 10 | io.on("connection", function (socket) { 11 | console.log("a user connected"); 12 | 13 | //响应某用户发送消息 14 | socket.on("update", function (data) { 15 | console.log("update:" + data); 16 | i = data 17 | // 广播给所有人 18 | io.emit("data", data); 19 | }); 20 | setInterval(() => { 21 | io.emit("data", i++); 22 | }, 1000); 23 | 24 | socket.on("disconnect", function () { 25 | console.log("user disconnected"); 26 | }); 27 | }); 28 | 29 | http.listen(3000, function () { 30 | console.log("listening on *:3000"); 31 | }); 32 | -------------------------------------------------------------------------------- /vuex/03-advanced/03-plugins/02-websocket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "02-websocket", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "express": "^4.17.1", 8 | "koa": "^2.13.1", 9 | "koa-static": "^5.0.0", 10 | "koa-websocket": "^6.0.0", 11 | "socket.io": "^3.1.2" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC" 20 | } 21 | -------------------------------------------------------------------------------- /vuex/03-advanced/04-strict-mode/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /vuex/03-advanced/05-form/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/api/shop.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getProducts: () => { 3 | return { 4 | name: "product01", 5 | }; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/demo/moduleA.js: -------------------------------------------------------------------------------- 1 | export default 'haha' -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/demo/test.js: -------------------------------------------------------------------------------- 1 | import str from './moduleA' 2 | console.log('babel ok') 3 | console.log('ModuelA ' + str) 4 | 5 | import { expect } from "chai"; 6 | 7 | describe("mutations", () => { 8 | it("INCREMENT", () => { 9 | expect(1).to.equal(1); 10 | }); 11 | }); -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "06-testing", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack-dev-server --mode development --config webpack.dev.config.js", 8 | "build": "webpack", 9 | "test": "webpack && mocha dist/test-bundle.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@babel/core": "^7.13.10", 16 | "babel-loader": "^8.2.2", 17 | "chai": "^4.3.4", 18 | "inject-loader": "^4.0.1", 19 | "mocha": "^8.3.2", 20 | "mocha-loader": "^5.1.5", 21 | "sinon": "^10.0.0", 22 | "vue": "^3.0.7", 23 | "vuex": "^4.0.0", 24 | "webpack": "^5.27.2", 25 | "webpack-cli": "^4.5.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/preset-env": "^7.13.12" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/__tests__/actions.spec.js: -------------------------------------------------------------------------------- 1 | // 使用 require 语法处理内联 loaders。 2 | // inject-loader 返回一个允许我们注入 mock 依赖的模块工厂 3 | import { expect } from "chai"; 4 | // const inject = require('inject-loader!./hello.js') 5 | // const actionsInjector = require("inject-loader!../actions"); 6 | 7 | // 使用 mocks 创建模块 8 | // const actions = actionsInjector({ 9 | // "../../api/shop": { 10 | // getProducts(cb) { 11 | // setTimeout(() => { 12 | // cb([ 13 | // /* mocked response */ 14 | // ]); 15 | // }, 100); 16 | // }, 17 | // }, 18 | // }); 19 | // import sinon from 'sinon' 20 | // import actions from "../actions"; 21 | 22 | // describe("actions", () => { 23 | // it("getAllProducts", () => { 24 | // const commit = sinon.spy(); 25 | // const state = {}; 26 | 27 | // actions.getAllProducts({ commit, state }); 28 | 29 | // expect(commit.args).to.deep.equal([ 30 | // ["REQUEST_PRODUCTS"], 31 | // [ 32 | // "RECEIVE_PRODUCTS", 33 | // { 34 | // /* mocked response */ 35 | // }, 36 | // ], 37 | // ]); 38 | // }); 39 | // }); 40 | 41 | // // 用指定的 mutations 测试 action 的辅助函数 42 | // const testAction = (action, args, state, expectedMutations, done) => { 43 | // let count = 0; 44 | 45 | // // 模拟提交 46 | // const commit = (type, payload) => { 47 | // const mutation = expectedMutations[count]; 48 | 49 | // try { 50 | // expect(mutation.type).to.equal(type); 51 | // expect(mutation.payload).to.deep.equal(payload); 52 | // } catch (error) { 53 | // done(error); 54 | // } 55 | 56 | // count++; 57 | // if (count >= expectedMutations.length) { 58 | // done(); 59 | // } 60 | // }; 61 | 62 | // // 用模拟的 store 和参数调用 action 63 | // action({ commit, state }, ...args); 64 | 65 | // // 检查是否没有 mutation 被 dispatch 66 | // if (expectedMutations.length === 0) { 67 | // expect(count).to.equal(0); 68 | // done(); 69 | // } 70 | // }; 71 | 72 | // describe("actions", () => { 73 | // it("getAllProducts", (done) => { 74 | // testAction( 75 | // actions.getAllProducts, 76 | // [], 77 | // {}, 78 | // [ 79 | // { type: "REQUEST_PRODUCTS" }, 80 | // { 81 | // type: "RECEIVE_PRODUCTS", 82 | // payload: { 83 | // /* mocked response */ 84 | // }, 85 | // }, 86 | // ], 87 | // done 88 | // ); 89 | // }); 90 | // }); 91 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/__tests__/getters.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { getters } from '../getters' 3 | 4 | describe('getters', () => { 5 | it('filteredProducts', () => { 6 | // 模拟状态 7 | const state = { 8 | products: [ 9 | { id: 1, title: 'Apple', category: 'fruit' }, 10 | { id: 2, title: 'Orange', category: 'fruit' }, 11 | { id: 3, title: 'Carrot', category: 'vegetable' } 12 | ] 13 | } 14 | // 模拟 getter 15 | const filterCategory = 'fruit' 16 | 17 | // 获取 getter 的结果 18 | const result = getters.filteredProducts(state, { filterCategory }) 19 | 20 | // 断言结果 21 | expect(result).to.deep.equal([ 22 | { id: 1, title: 'Apple', category: 'fruit' }, 23 | { id: 2, title: 'Orange', category: 'fruit' } 24 | ]) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/__tests__/hello.js: -------------------------------------------------------------------------------- 1 | // export default { 2 | // str: 'abc' 3 | // } 4 | 5 | export const a = 10 6 | 7 | // module.exports = { 8 | // str: "abc", 9 | // }; 10 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/__tests__/index.js: -------------------------------------------------------------------------------- 1 | 2 | import mutations from './mutations.spec' 3 | import getters from './getters.spec' 4 | import actions from './actions.spec.js' -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/__tests__/mutations.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { mutations } from "../store"; 3 | 4 | // destructure assign `mutations` 5 | const { increment } = mutations; 6 | console.log('increment',increment) 7 | describe("mutations", () => { 8 | it("INCREMENT", () => { 9 | // mock state 10 | const state = { count: 0 }; 11 | // apply mutation 12 | increment(state); 13 | // assert result 14 | expect(state.count).to.equal(1); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/actions.js: -------------------------------------------------------------------------------- 1 | import shop from "../api/shop"; 2 | 3 | export default { 4 | getAllProducts: ({ commit }) => { 5 | commit("REQUEST_PRODUCTS"); 6 | shop.getProducts((products) => { 7 | commit("RECEIVE_PRODUCTS", products); 8 | }); 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/getters.js: -------------------------------------------------------------------------------- 1 | export const getters = { 2 | filteredProducts(state, { filterCategory }) { 3 | return state.products.filter((product) => { 4 | return product.category === filterCategory; 5 | }); 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/store/store.js: -------------------------------------------------------------------------------- 1 | import { createStore } from "vuex"; 2 | 3 | const state = { 4 | count: 0, 5 | products: [ 6 | { id: 1, title: "Apple", category: "fruit" }, 7 | { id: 2, title: "Orange", category: "fruit" }, 8 | { id: 3, title: "Carrot", category: "vegetable" }, 9 | ], 10 | }; 11 | 12 | export const mutations = { 13 | increment: (state) => state.count++, 14 | }; 15 | 16 | 17 | export default createStore({ 18 | state, 19 | mutations, 20 | }); 21 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // entry: "./test.js", 3 | entry:'./store/__tests__/index.js', 4 | mode:'development', 5 | output: { 6 | path: __dirname, 7 | filename: "./dist/test-bundle.js", 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | loader: "babel-loader", 14 | exclude: /node_modules/, 15 | }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /vuex/03-advanced/06-testing/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // entry: "./test.js", 3 | entry:'mocha-loader!babel-loader!./store/__tests__/index.js', 4 | mode:'development', 5 | output: { 6 | path: __dirname, 7 | filename: "./dist/test-bundle.js", 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | loader: "babel-loader", 14 | exclude: /node_modules/, 15 | }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/02-composition-api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Document 11 | 12 | 13 |
14 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/README.md: -------------------------------------------------------------------------------- 1 | # vuex-ts 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex-ts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.6.5", 11 | "vue": "^3.0.0", 12 | "vue-class-component": "^8.0.0-0", 13 | "vue-router": "^4.0.0-0", 14 | "vuex": "^4.0.0-0" 15 | }, 16 | "devDependencies": { 17 | "@vue/cli-plugin-babel": "~4.5.0", 18 | "@vue/cli-plugin-router": "~4.5.0", 19 | "@vue/cli-plugin-typescript": "~4.5.0", 20 | "@vue/cli-plugin-vuex": "~4.5.0", 21 | "@vue/cli-service": "~4.5.0", 22 | "@vue/compiler-sfc": "^3.0.0", 23 | "typescript": "~4.1.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vuex/03-advanced/08-typescript/public/favicon.ico -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 20 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vuex/03-advanced/08-typescript/src/assets/logo.png -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 47 | 48 | 49 | 65 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import { store, key } from "./store"; 5 | 6 | createApp(App) 7 | .use(store, key) 8 | .use(router) 9 | .mount("#app"); 10 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' 2 | import Home from '../views/Home.vue' 3 | 4 | const routes: Array = [ 5 | { 6 | path: '/', 7 | name: 'Home', 8 | component: Home 9 | }, 10 | { 11 | path: '/about', 12 | name: 'About', 13 | // route level code-splitting 14 | // this generates a separate chunk (about.[hash].js) for this route 15 | // which is lazy-loaded when the route is visited. 16 | component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 17 | } 18 | ] 19 | 20 | const router = createRouter({ 21 | history: createWebHistory(process.env.BASE_URL), 22 | routes 23 | }) 24 | 25 | export default router 26 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey } from "vue"; 2 | import { createStore, Store ,useStore} from "vuex"; 3 | 4 | // define your typings for the store state 5 | export interface State { 6 | count: number; 7 | } 8 | 9 | // define injection key 10 | export const key: InjectionKey> = Symbol(); 11 | 12 | export const store = createStore({ 13 | state: { 14 | count: 0, 15 | }, 16 | }); 17 | 18 | // 简写方法 19 | export function useStoreWithKey () { 20 | return useStore(key) 21 | } -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/src/vuex.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentCustomProperties } from 'vue' 2 | import { Store } from 'vuex' 3 | 4 | declare module '@vue/runtime-core' { 5 | // declare your own store states 6 | interface State { 7 | count: number 8 | } 9 | 10 | // provide typings for `this.$store` 11 | interface ComponentCustomProperties { 12 | $store: Store 13 | } 14 | } -------------------------------------------------------------------------------- /vuex/03-advanced/08-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /vuex/README.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | > Vuex 4.0 英文 https://next.vuex.vuejs.org/ 4 | > 5 | > Vuex 3.0 中文 https://vuex.vuejs.org/zh/guide/ 6 | 7 | 8 | ## 困难解决 9 | ### CLI工具增加模板编译 10 | ```js 11 | // main.js 12 | import { createApp } from 'vue/dist/vue.esm-bundler' 13 | ``` 14 | 15 | ### 忽略语法检查 16 | vue.config.js 17 | ``` 18 | module.exports = { 19 | lintOnSave: false, 20 | }; 21 | 22 | ``` 23 | 24 | ## Start 25 | 26 | ``` 27 | npm i vite -s 28 | ``` 29 | 30 | 31 | 32 | ``` 33 | vue create 01-start 34 | # 选择Vue3.0 35 | 36 | # 安装vuex 37 | yarn add vuex@4.0.0 38 | 39 | ``` 40 | 41 | 42 | 43 | Main.js 44 | 45 | ```js 46 | import { createApp } from 'vue' 47 | import { createStore } from 'vuex' 48 | import App from "./App.vue"; 49 | 50 | // Create a new store instance. 51 | const store = createStore({ 52 | state () { 53 | return { 54 | count: 0 55 | } 56 | }, 57 | mutations: { 58 | increment (state) { 59 | state.count++ 60 | } 61 | } 62 | }) 63 | 64 | store.commit('increment') 65 | 66 | console.log(store.state.count) // -> 1 67 | 68 | const app = createApp(App) 69 | app.use(store) 70 | app.mount('#app') 71 | 72 | ``` 73 | 74 | HelloWorld.vue 75 | 76 | ``` 77 | 83 | 84 | 94 | ``` 95 | 96 | ## 核心概念 - Core Concepts 97 | 98 | ### State 99 | 100 | 101 | 102 | 下载Vuex 103 | 104 | ``` 105 | 106 | yarn --ignorescript 107 | ``` 108 | 109 | 110 | 111 | 112 | 113 | ## Advanced 114 | 115 | ### 01 Application Structure 116 | 117 | ### 02 Composition API 118 | 119 | ### 03 Plugins 120 | 121 | ### 04 Strict Mode - 严格模式 122 | 123 | ### 05 Form Handing - 表单处理 124 | 125 | ### 06 Testing 测试 126 | > mocha是一个功能丰富的javascript测试框架,运行在node.js和浏览器中,使异步测试变得简单有趣。Mocha测试连续运行,允许灵活和准确的报告,同时将未捕获的异常映射到正确的测试用例。在Github上托管。 127 | 128 | 129 | ### 07 Hot Reloading 热重载 130 | 131 | ### 08 TypeScript Support TS支持 132 | 133 | ## Migration Guide 迁移指南 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /vuex/source/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /vuex/source/README.md: -------------------------------------------------------------------------------- 1 | # vuex-start 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /vuex/source/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /vuex/source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuex-start", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "vue": "^3.0.0", 13 | "vuex": "4.0.0" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "~4.5.0", 17 | "@vue/cli-plugin-eslint": "~4.5.0", 18 | "@vue/cli-service": "~4.5.0", 19 | "@vue/compiler-sfc": "^3.0.0", 20 | "babel-eslint": "^10.1.0", 21 | "eslint": "^6.7.2", 22 | "eslint-plugin-vue": "^7.0.0-0" 23 | }, 24 | "eslintConfig": { 25 | "root": true, 26 | "env": { 27 | "node": true 28 | }, 29 | "extends": [ 30 | "plugin:vue/vue3-essential", 31 | "eslint:recommended" 32 | ], 33 | "parserOptions": { 34 | "parser": "babel-eslint" 35 | }, 36 | "rules": {} 37 | }, 38 | "browserslist": [ 39 | "> 1%", 40 | "last 2 versions", 41 | "not dead" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /vuex/source/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vuex/source/public/favicon.ico -------------------------------------------------------------------------------- /vuex/source/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /vuex/source/src/App.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /vuex/source/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/su37josephxia/learn-vue3/49981acb1816e5e8a02da86896206c50b9839724/vuex/source/src/assets/logo.png -------------------------------------------------------------------------------- /vuex/source/src/components/Getter.js: -------------------------------------------------------------------------------- 1 | import { mapGetters } from "vuex"; 2 | export default { 3 | template: ` 4 |

{{ $store.getters.doneTodos }}

5 |

{{ $store.getters.doneTodosCount }}

6 |

{{ doneTodosCount }}

7 |

{{ doneCount }}

8 | `, 9 | // 展开运算符 10 | computed: { 11 | // doneTodosCount() { 12 | // return this.$store.getters.doneTodosCount; 13 | // }, 14 | ...mapGetters(["doneTodosCount"]), 15 | ...mapGetters({ 16 | doneCount: "doneTodosCount", 17 | }), 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /vuex/source/src/components/HelloWorld.js: -------------------------------------------------------------------------------- 1 | export default { 2 | template: ` 3 | 4 |

{{ $store.state.count }}

5 | `, 6 | methods: { 7 | increment() { 8 | this.$store.commit("increment"); 9 | console.log(this.$store.state.count); 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /vuex/source/src/components/Mutation.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | template: ` 4 |
5 | {{ $store.state.count }} 6 |
7 | `, 8 | mounted() { 9 | setTimeout(() => { 10 | this.$store.commit("increment"); 11 | }, 1000); 12 | setTimeout(() => { 13 | this.$store.commit("incrementN", 100); 14 | }, 2000); 15 | setTimeout(() => { 16 | this.$store.commit("incrementO", { 17 | amount: 300, 18 | }); 19 | }, 3000); 20 | setTimeout(() => { 21 | this.$store.commit({ 22 | type: "incrementO", 23 | amount: 200, 24 | }); 25 | }, 4000); 26 | }, 27 | }; -------------------------------------------------------------------------------- /vuex/source/src/components/State.js: -------------------------------------------------------------------------------- 1 | import { mapState } from "vuex"; 2 | 3 | export default { 4 | template: ` 5 |
{{ count }}
6 |
{{ countPlusLocalState }}
7 |
{{ countAlias }}
8 |
{{ localComputed }}
9 | `, 10 | // computed: { 11 | // count() { 12 | // console.log("computed", this.$store.state.count); 13 | // return this.$store.state.count; 14 | // }, 15 | // }, 16 | 17 | data() { 18 | return { 19 | localCount: 100, 20 | }; 21 | }, 22 | // MapState 23 | // computed: mapState({ 24 | // // arrow functions can make the code very succinct! 25 | // count: (state) => state.count, 26 | 27 | // // passing the string value 'count' is same as `state => state.count` 28 | // countAlias: "count", 29 | 30 | // // to access local state with `this`, a normal function must be used 31 | // countPlusLocalState(state) { 32 | // return state.count + this.localCount; 33 | // }, 34 | // }), 35 | 36 | // 简写 37 | // computed: mapState([ 38 | // // map this.count to store.state.count 39 | // "count", 40 | // ]), 41 | 42 | // 展开运算符 43 | computed: { 44 | localComputed() { 45 | return 100; 46 | }, 47 | ...mapState(["count"]), 48 | }, 49 | 50 | mounted() { 51 | console.log("monted..."); 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /vuex/source/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue/dist/vue.esm-bundler' 2 | import { createStore } from 'vuex' 3 | // import App from "./App.vue"; 4 | 5 | import HelloWorld from './components/HelloWorld' 6 | 7 | // Create a new store instance. 8 | const store = createStore({ 9 | state () { 10 | return { 11 | count: 0, 12 | todos: [ 13 | { id: 1, text: '...', done: true }, 14 | { id: 2, text: '...', done: false } 15 | ] 16 | } 17 | }, 18 | 19 | mutations: { 20 | increment (state) { 21 | state.count++ 22 | }, 23 | incrementN (state,n ) { 24 | state.count += n 25 | }, 26 | incrementO (state,payload) { 27 | state.count += payload.amount 28 | } 29 | }, 30 | 31 | getters: { 32 | doneTodos: state => { 33 | return state.todos.filter(todo => todo.done) 34 | }, 35 | doneTodosCount: (state, getters) => { 36 | return getters.doneTodos.length 37 | } 38 | } 39 | }) 40 | 41 | store.commit('increment') 42 | 43 | console.log(store.state.count) // -> 1 44 | 45 | const app = createApp(HelloWorld) 46 | // app.store = store 47 | app.use(store) 48 | app.mount('#app') 49 | 50 | 51 | -------------------------------------------------------------------------------- /vuex/source/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: false, 3 | }; 4 | --------------------------------------------------------------------------------