├── .vscode ├── settings.json └── launch.json ├── codes ├── snabbdom │ ├── .gitignore │ ├── src │ │ ├── utils.js │ │ ├── export.js │ │ ├── modules │ │ │ ├── class.js │ │ │ ├── props.js │ │ │ └── style.js │ │ ├── h.js │ │ ├── vnode.js │ │ └── domApi.js │ ├── package.json │ ├── demo │ │ ├── index.ejs │ │ └── app.js │ ├── README.md │ ├── webpack.config.js │ └── LICENSE ├── minipack │ ├── .gitignore │ ├── demo │ │ ├── msg.js │ │ ├── entry.js │ │ └── hi.js │ ├── README.md │ ├── package.json │ ├── LICENSE │ └── src │ │ └── index.js ├── vue │ ├── .gitignore │ ├── src │ │ ├── core │ │ │ ├── components │ │ │ │ ├── index.js │ │ │ │ └── keep-alive.js │ │ │ ├── vdom │ │ │ │ ├── helpers │ │ │ │ │ ├── is-async-placeholder.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── get-first-component-child.js │ │ │ │ │ ├── merge-hook.js │ │ │ │ │ ├── extract-props.js │ │ │ │ │ ├── update-listeners.js │ │ │ │ │ ├── normalize-children.js │ │ │ │ │ └── resolve-async-component.js │ │ │ │ ├── modules │ │ │ │ │ ├── index.js │ │ │ │ │ ├── ref.js │ │ │ │ │ └── directives.js │ │ │ │ ├── vnode.js │ │ │ │ ├── create-functional-component.js │ │ │ │ ├── create-element.js │ │ │ │ └── create-component.js │ │ │ ├── global-api │ │ │ │ ├── mixin.js │ │ │ │ ├── use.js │ │ │ │ ├── assets.js │ │ │ │ ├── index.js │ │ │ │ └── extend.js │ │ │ ├── util │ │ │ │ ├── index.js │ │ │ │ ├── perf.js │ │ │ │ ├── lang.js │ │ │ │ ├── error.js │ │ │ │ ├── env.js │ │ │ │ ├── debug.js │ │ │ │ ├── next-tick.js │ │ │ │ └── props.js │ │ │ ├── instance │ │ │ │ ├── render-helpers │ │ │ │ │ ├── resolve-filter.js │ │ │ │ │ ├── bind-object-listeners.js │ │ │ │ │ ├── render-list.js │ │ │ │ │ ├── check-keycodes.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── render-slot.js │ │ │ │ │ ├── bind-object-props.js │ │ │ │ │ ├── render-static.js │ │ │ │ │ └── resolve-slots.js │ │ │ │ ├── index.js │ │ │ │ ├── inject.js │ │ │ │ ├── proxy.js │ │ │ │ ├── events.js │ │ │ │ ├── render.js │ │ │ │ └── init.js │ │ │ ├── index.js │ │ │ ├── observer │ │ │ │ ├── array.js │ │ │ │ ├── traverse.js │ │ │ │ ├── dep.js │ │ │ │ ├── scheduler.js │ │ │ │ ├── watcher.js │ │ │ │ ├── index.js │ │ │ │ └── readme.md │ │ │ └── config.js │ │ └── shared │ │ │ ├── constants.js │ │ │ └── util.js │ ├── .editorconfig │ ├── .babelrc │ ├── package.json │ └── __test_observer.js └── didact │ ├── .gitignore │ ├── test │ ├── _browser-mock.js │ ├── 02.re-render-element.test.js │ ├── 03.reconciliation.test.js │ ├── 05.set-state.test.js │ ├── 01.render-jsx-dom-elements.test.js │ ├── 04.components.test.js │ └── 00.render-dom-elements.test.js │ ├── src │ ├── index.ts │ ├── element.ts │ ├── component.ts │ ├── interface.ts │ └── dom-utils.ts │ ├── .babelrc │ ├── tslint.json │ ├── package.json │ ├── LICENSE │ └── tsconfig.json ├── examples ├── webpack-demo │ ├── README.md │ ├── .gitignore │ ├── src │ │ ├── js │ │ │ ├── info.js │ │ │ ├── constant.js │ │ │ └── title.js │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── logo.svg │ │ ├── styles │ │ │ └── index.scss │ │ ├── template.html │ │ └── index.js │ ├── .babelrc.json │ ├── .prettierrc.json │ ├── postcss.config.js │ ├── config │ │ ├── paths.js │ │ ├── webpack.dev.js │ │ ├── webpack.prod.js │ │ └── webpack.common.js │ ├── LICENSE │ ├── .eslintrc.json │ └── package.json └── README.md ├── .gitignore ├── README.md ├── LICENSE.md └── sites_and_articles.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /codes/snabbdom/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /examples/webpack-demo/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codes/minipack/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | > examples for blog. -------------------------------------------------------------------------------- /codes/vue/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /codes/minipack/demo/msg.js: -------------------------------------------------------------------------------- 1 | export default 'Hello'; 2 | -------------------------------------------------------------------------------- /codes/didact/.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | node_modules/ 3 | package-lock.json -------------------------------------------------------------------------------- /examples/webpack-demo/.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | /node_modules 3 | /dist -------------------------------------------------------------------------------- /codes/minipack/demo/entry.js: -------------------------------------------------------------------------------- 1 | import sayHi from './hi'; 2 | 3 | sayHi('Jack'); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .DS_Store 4 | 5 | # fecs config 6 | .fecsrc 7 | .fecsignore -------------------------------------------------------------------------------- /codes/minipack/README.md: -------------------------------------------------------------------------------- 1 | [Ronen Amiel: **Build your own webpack**](https://www.youtube.com/watch?v=Gc9-7PBqOC8) [39:38] -------------------------------------------------------------------------------- /codes/vue/src/core/components/index.js: -------------------------------------------------------------------------------- 1 | import KeepAlive from './keep-alive' 2 | 3 | export default { 4 | KeepAlive 5 | } 6 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/js/info.js: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: "off" */ 2 | export const text = '2021/02/01' 3 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/js/constant.js: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: "off" */ 2 | export const author = 'Creeper' 3 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creeperyang/blog/HEAD/examples/webpack-demo/src/images/favicon.png -------------------------------------------------------------------------------- /examples/webpack-demo/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": ["@babel/plugin-proposal-class-properties"] 4 | } 5 | -------------------------------------------------------------------------------- /codes/minipack/demo/hi.js: -------------------------------------------------------------------------------- 1 | import message from './msg'; 2 | 3 | export default function sayHi(who) { 4 | console.log(`> ${message} ${who}!`); 5 | } 6 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/is-async-placeholder.js: -------------------------------------------------------------------------------- 1 | export function isAsyncPlaceholder(node) { 2 | return node.isComment && node.asyncFactory 3 | } 4 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/js/title.js: -------------------------------------------------------------------------------- 1 | /* eslint import/prefer-default-export: "off" */ 2 | export const getTitle = () => { 3 | return '柴门闻犬吠,风雪夜归人。' 4 | } 5 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/modules/index.js: -------------------------------------------------------------------------------- 1 | import directives from './directives' 2 | import ref from './ref' 3 | 4 | export default [ 5 | ref, 6 | directives 7 | ] 8 | -------------------------------------------------------------------------------- /examples/webpack-demo/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "printWidth": 80, 6 | "semi": false 7 | } 8 | -------------------------------------------------------------------------------- /examples/webpack-demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-preset-env': { 4 | browsers: 'last 2 versions', 5 | }, 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /codes/vue/src/core/global-api/mixin.js: -------------------------------------------------------------------------------- 1 | import { 2 | mergeOptions 3 | } from '../util/index' 4 | 5 | export function initMixin(Vue) { 6 | Vue.mixin = function (mixin) { 7 | this.options = mergeOptions(this.options, mixin) 8 | return this 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /codes/didact/test/_browser-mock.js: -------------------------------------------------------------------------------- 1 | import browserEnv from 'browser-env'; 2 | 3 | browserEnv(['document']); 4 | 5 | const deadline = { 6 | timeRemaining: () => 1000, 7 | }; 8 | 9 | global.requestIdleCallback = function requestIdleCallback(fn) { 10 | fn(deadline); 11 | }; 12 | -------------------------------------------------------------------------------- /codes/vue/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /codes/didact/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component'; 2 | import { createElement } from './element'; 3 | import { render } from './reconciler'; 4 | 5 | export default { 6 | createElement, 7 | Component, 8 | render, 9 | }; 10 | 11 | export { createElement, Component, render }; 12 | -------------------------------------------------------------------------------- /codes/vue/src/core/util/index.js: -------------------------------------------------------------------------------- 1 | export * from '../../shared/util' 2 | export * from './lang' 3 | export * from './env' 4 | export * from './options' 5 | export * from './debug' 6 | export * from './props' 7 | export * from './error' 8 | export * from './next-tick' 9 | export { defineReactive } from '../observer/index' 10 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 6 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 7 | text-align: center; 8 | background-color: #000; 9 | color: #fff; 10 | } 11 | -------------------------------------------------------------------------------- /codes/didact/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-react-jsx" 4 | ], 5 | "presets": [ 6 | [ 7 | "@babel/preset-env", 8 | { 9 | "targets": { 10 | "node": "current" 11 | } 12 | } 13 | ] 14 | ] 15 | } -------------------------------------------------------------------------------- /codes/snabbdom/src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 一些工具函数 3 | */ 4 | 5 | export const isArray = Array.isArray 6 | 7 | export const isPrimitive = val => { 8 | const type = typeof val 9 | return type === 'number' || type === 'string' 10 | } 11 | 12 | export const flattenArray = array => { 13 | return Array.prototype.concat.apply([], array) 14 | } 15 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/resolve-filter.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { identity, resolveAsset } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for resolving filters 7 | */ 8 | export function resolveFilter (id ) { 9 | return resolveAsset(this.$options, 'filters', id, true) || identity 10 | } 11 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | export * from './merge-hook' 4 | export * from './extract-props' 5 | export * from './update-listeners' 6 | export * from './normalize-children' 7 | export * from './resolve-async-component' 8 | export * from './get-first-component-child' 9 | export * from './is-async-placeholder' 10 | -------------------------------------------------------------------------------- /examples/webpack-demo/config/paths.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | // Source files 5 | src: path.resolve(__dirname, '../src'), 6 | 7 | // Production build files 8 | build: path.resolve(__dirname, '../dist'), 9 | 10 | // Static files that get copied to build folder 11 | public: path.resolve(__dirname, '../public'), 12 | } 13 | -------------------------------------------------------------------------------- /codes/vue/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "modules": "commonjs", 7 | "targets": { 8 | "node": "current" 9 | } 10 | } 11 | ], 12 | ["@babel/preset-stage-2", { 13 | "decoratorsLegacy": true 14 | }] 15 | ], 16 | "plugins": [ 17 | "@babel/plugin-transform-runtime" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /codes/snabbdom/src/export.js: -------------------------------------------------------------------------------- 1 | import init from './index' 2 | import h from './h' 3 | import propsModule from './modules/props' 4 | import styleModule from './modules/style' 5 | import classModule from './modules/class' 6 | 7 | const patch = init([ 8 | classModule, 9 | propsModule, 10 | styleModule 11 | ]) 12 | 13 | export const snabbdomBuddle = { patch, h } 14 | export default snabbdomBuddle -------------------------------------------------------------------------------- /examples/webpack-demo/src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "测试 vue-observer", 11 | "program": "${workspaceFolder}/codes/vue/__test_observer.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /codes/vue/src/shared/constants.js: -------------------------------------------------------------------------------- 1 | export const SSR_ATTR = 'data-server-rendered' 2 | 3 | export const ASSET_TYPES = [ 4 | 'component', 5 | 'directive', 6 | 'filter' 7 | ] 8 | 9 | export const LIFECYCLE_HOOKS = [ 10 | 'beforeCreate', 11 | 'created', 12 | 'beforeMount', 13 | 'mounted', 14 | 'beforeUpdate', 15 | 'updated', 16 | 'beforeDestroy', 17 | 'destroyed', 18 | 'activated', 19 | 'deactivated', 20 | 'errorCaptured' 21 | ] 22 | -------------------------------------------------------------------------------- /codes/minipack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minipack", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "node src/index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@babel/core": "^7.1.2", 14 | "@babel/parser": "^7.1.3", 15 | "@babel/preset-env": "^7.1.0", 16 | "@babel/traverse": "^7.1.4" 17 | }, 18 | "devDependencies": { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/get-first-component-child.js: -------------------------------------------------------------------------------- 1 | import { 2 | isDef 3 | } from '../../../shared/util' 4 | import { 5 | isAsyncPlaceholder 6 | } from './is-async-placeholder' 7 | 8 | export function getFirstComponentChild(children) { 9 | if (Array.isArray(children)) { 10 | for (let i = 0; i < children.length; i++) { 11 | const c = children[i] 12 | if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) { 13 | return c 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /codes/snabbdom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snabbdom-t", 3 | "version": "0.6.7", 4 | "description": "deep into snabbdom", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "build": "webpack --display-modules --display-optimization-bailout", 8 | "start": "webpack-dev-server" 9 | }, 10 | "keywords": [], 11 | "author": "creeperyang", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "html-webpack-plugin": "^2.28.0", 15 | "webpack": "^3.10.0", 16 | "webpack-dev-server": "^2.9.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /codes/snabbdom/demo/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /codes/snabbdom/README.md: -------------------------------------------------------------------------------- 1 | # snabbdom 源码解析 2 | 3 | snabbdom 的 ES6 版本(有细微不同点),核心代码有详细注释,主要用于理解 snabbdom 。 4 | 5 | 代码结构: 6 | 7 | ```bash 8 | src 9 | ├── domApi.js # dom api,主要是各种 DOM 操作的包装,快速浏览即可。 10 | ├── export.js # export,决定暴露什么接口给调用者,可忽略。 11 | ├── h.js # `h()`帮助函数,很简单。 12 | ├── index.js # 核心代码,Virtual DOM 的 diff 实现,从 Virtual DOM 构建 DOM 等等。 13 | ├── modules # 各个模块,主要负责属性处理。 14 | │ ├── class.js 15 | │ ├── props.js 16 | │ └── style.js 17 | ├── utils.js # util 函数。 18 | └── vnode.js # vnode 定义和一些相关函数。 19 | ``` 20 | 21 | 看 snabbdom 的实际例子: 22 | 23 | ```bash 24 | npm i && npm run start 25 | ``` -------------------------------------------------------------------------------- /codes/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-fake", 3 | "version": "1.0.0", 4 | "description": "vue insight", 5 | "main": "src/core/index.js", 6 | "scripts": { 7 | "build": "rm -rf dist && babel src --out-dir dist" 8 | }, 9 | "license": "MIT", 10 | "devDependencies": { 11 | "@babel/cli": "^7.0.0-beta.51", 12 | "@babel/core": "^7.0.0-beta.51", 13 | "@babel/plugin-transform-runtime": "^7.0.0-beta.51", 14 | "@babel/preset-env": "^7.0.0-beta.51", 15 | "@babel/preset-stage-2": "^7.0.0-beta.51" 16 | }, 17 | "dependencies": { 18 | "@babel/runtime": "^7.0.0-beta.51" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /codes/didact/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "object-literal-sort-keys": false, 9 | "quotemark": [ 10 | true, 11 | "single" 12 | ], 13 | "one-line": [ 14 | true, 15 | "check-open-brace", 16 | "check-whitespace" 17 | ], 18 | "no-bitwise": false, 19 | "object-literal-key-quotes": [ 20 | true, 21 | "as-needed" 22 | ] 23 | }, 24 | "rulesDirectory": [] 25 | } -------------------------------------------------------------------------------- /codes/vue/src/core/util/perf.js: -------------------------------------------------------------------------------- 1 | import { inBrowser } from './env' 2 | 3 | export let mark 4 | export let measure 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | const perf = inBrowser && window.performance 8 | /* istanbul ignore if */ 9 | if ( 10 | perf && 11 | perf.mark && 12 | perf.measure && 13 | perf.clearMarks && 14 | perf.clearMeasures 15 | ) { 16 | mark = tag => perf.mark(tag) 17 | measure = (name, startTag, endTag) => { 18 | perf.measure(name, startTag, endTag) 19 | perf.clearMarks(startTag) 20 | perf.clearMarks(endTag) 21 | perf.clearMeasures(name) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /codes/vue/__test_observer.js: -------------------------------------------------------------------------------- 1 | const { observe } = require('./dist/core/observer') 2 | const Watcher = require('./dist/core/observer/watcher').default 3 | 4 | function createWatcher(data, expOrFn, cb, options) { 5 | const vm = { 6 | data: data || {}, 7 | _watchers: [] 8 | } 9 | observe(vm.data, true) 10 | return new Watcher(vm, expOrFn, cb, options) 11 | } 12 | 13 | const raw = { 14 | s: 'hi', 15 | n: 100, 16 | o: {x: 1, arr: [1, 2]}, 17 | arr: [8, 9] 18 | } 19 | 20 | const w = createWatcher(raw, function () { 21 | return this.data.arr 22 | }, (a, b) => { 23 | console.log('--->', a, b) 24 | }, { 25 | deep: false, 26 | sync: true 27 | }) 28 | 29 | raw.arr.push(10) 30 | -------------------------------------------------------------------------------- /codes/didact/src/element.ts: -------------------------------------------------------------------------------- 1 | import { IProps } from './interface'; 2 | 3 | export const TEXT_ELEMENT = 'TEXT ELEMENT'; 4 | 5 | export function createElement(type: string, config: object, ...args: any[]) { 6 | const props: IProps = Object.assign({}, config); 7 | const hasChildren = args.length > 0; 8 | const rawChildren = hasChildren ? [].concat(...args) : []; 9 | props.children = rawChildren 10 | .filter((c) => c != null && c !== false) 11 | .map((c: any) => c instanceof Object ? c : createTextElement(c)); 12 | return { type, props }; 13 | } 14 | 15 | function createTextElement(value: string) { 16 | return createElement(TEXT_ELEMENT, { nodeValue: value }); 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | creeperyang's blog 4 | 5 |

6 | 7 |

8 | 所有文章 9 |

10 | 11 |
12 | 13 |

关于订阅

14 | 15 |

喜欢请点右上角 `star`。订阅的话,请 `watch` 按钮。

16 | 17 |

转载注意事项

18 | 19 |

除注明外,所有文章均采用Creative Commons BY-NC-ND 4.0(自由转载-保持署名-非商用-禁止演绎)协议发布。

20 | 21 | -------------------------------------------------------------------------------- /codes/snabbdom/webpack.config.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | 4 | module.exports = [ 5 | { 6 | entry: './src/export.js', 7 | output: { 8 | libraryTarget: 'umd', 9 | libraryExport: 'default', 10 | library: 'snabbdom', 11 | filename: 'snabbdom.js', 12 | path: join(__dirname, 'dist') 13 | } 14 | }, 15 | { 16 | entry: './demo/app.js', 17 | output: { 18 | filename: 'bundle.js', 19 | path: join(__dirname, 'dist') 20 | }, 21 | plugins: [ 22 | new HtmlWebpackPlugin({ 23 | title: 'snabbdom', 24 | template: 'demo/index.ejs' 25 | }) 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /codes/vue/src/core/global-api/use.js: -------------------------------------------------------------------------------- 1 | import { 2 | toArray 3 | } from '../util/index' 4 | 5 | export function initUse(Vue) { 6 | Vue.use = function (plugin) { 7 | const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) 8 | if (installedPlugins.indexOf(plugin) > -1) { 9 | return this 10 | } 11 | 12 | // additional parameters 13 | const args = toArray(arguments, 1) 14 | args.unshift(this) 15 | if (typeof plugin.install === 'function') { 16 | plugin.install.apply(plugin, args) 17 | } else if (typeof plugin === 'function') { 18 | plugin.apply(null, args) 19 | } 20 | installedPlugins.push(plugin) 21 | return this 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /codes/didact/test/02.re-render-element.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | /** @jsx createElement */ 4 | import { render, createElement } from '../lib'; 5 | 6 | test.beforeEach(t => { 7 | let root = document.getElementById('root'); 8 | if (!root) { 9 | root = document.createElement('div'); 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | } 13 | t.context.root = root; 14 | }); 15 | 16 | test('render jsx div', t => { 17 | const root = t.context.root; 18 | const element =
Foo
; 19 | render(element, root); 20 | t.is(root.innerHTML, '
Foo
'); 21 | render(element, root); 22 | t.is(root.innerHTML, '
Foo
'); 23 | }); -------------------------------------------------------------------------------- /codes/vue/src/core/instance/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | initMixin 3 | } from './init' 4 | import { 5 | stateMixin 6 | } from './state' 7 | import { 8 | renderMixin 9 | } from './render' 10 | import { 11 | eventsMixin 12 | } from './events' 13 | import { 14 | lifecycleMixin 15 | } from './lifecycle' 16 | import { 17 | warn 18 | } from '../util/index' 19 | 20 | function Vue(options) { 21 | if (process.env.NODE_ENV !== 'production' && 22 | !(this instanceof Vue) 23 | ) { 24 | warn('Vue is a constructor and should be called with the `new` keyword') 25 | } 26 | this._init(options) 27 | } 28 | 29 | initMixin(Vue) 30 | stateMixin(Vue) 31 | eventsMixin(Vue) 32 | lifecycleMixin(Vue) 33 | renderMixin(Vue) 34 | 35 | export default Vue 36 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/bind-object-listeners.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { warn, extend, isPlainObject } from 'core/util/index' 4 | 5 | export function bindObjectListeners (data , value ) { 6 | if (value) { 7 | if (!isPlainObject(value)) { 8 | process.env.NODE_ENV !== 'production' && warn( 9 | 'v-on without argument expects an Object value', 10 | this 11 | ) 12 | } else { 13 | const on = data.on = data.on ? extend({}, data.on) : {} 14 | for (const key in value) { 15 | const existing = on[key] 16 | const ours = value[key] 17 | on[key] = existing ? [].concat(existing, ours) : ours 18 | } 19 | } 20 | } 21 | return data 22 | } 23 | -------------------------------------------------------------------------------- /codes/vue/src/core/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/index' 2 | import { initGlobalAPI } from './global-api/index' 3 | import { isServerRendering } from 'core/util/env' 4 | import { FunctionalRenderContext } from 'core/vdom/create-functional-component' 5 | 6 | initGlobalAPI(Vue) 7 | 8 | Object.defineProperty(Vue.prototype, '$isServer', { 9 | get: isServerRendering 10 | }) 11 | 12 | Object.defineProperty(Vue.prototype, '$ssrContext', { 13 | get () { 14 | /* istanbul ignore next */ 15 | return this.$vnode && this.$vnode.ssrContext 16 | } 17 | }) 18 | 19 | // expose FunctionalRenderContext for ssr runtime helper installation 20 | Object.defineProperty(Vue, 'FunctionalRenderContext', { 21 | value: FunctionalRenderContext 22 | }) 23 | 24 | Vue.version = '__VERSION__' 25 | 26 | export default Vue 27 | -------------------------------------------------------------------------------- /examples/webpack-demo/src/index.js: -------------------------------------------------------------------------------- 1 | // Test import of a JavaScript function 2 | import { getTitle } from './js/title' 3 | 4 | // Test import of an asset 5 | import myLogo from './images/logo.svg' 6 | 7 | // Test import of styles 8 | import './styles/index.scss' 9 | 10 | const logo = document.createElement('img') 11 | logo.src = myLogo 12 | 13 | const heading = document.createElement('h1') 14 | heading.textContent = getTitle() 15 | 16 | const app = document.querySelector('#root') 17 | app.append(logo, heading) 18 | 19 | import('./js/info').then((v) => { 20 | const footer = document.createElement('footer') 21 | footer.textContent = v.text 22 | app.append(footer) 23 | }) 24 | 25 | import('./js/constant').then((v) => { 26 | const div = document.createElement('div') 27 | div.textContent = v.author 28 | app.append(div) 29 | }) 30 | -------------------------------------------------------------------------------- /codes/snabbdom/src/modules/class.js: -------------------------------------------------------------------------------- 1 | /** 2 | * class 模块:支持 vnode 使用 className 来操作 html class。 3 | */ 4 | 5 | import { isArray } from '../utils' 6 | 7 | function updateClassName(oldVnode, vnode) { 8 | const oldName = oldVnode.data.className 9 | const newName = vnode.data.className 10 | 11 | if (!oldName && !newName) return 12 | if (oldName === newName) return 13 | 14 | const elm = vnode.elm 15 | if (typeof newName === 'string' && newName) { 16 | elm.className = newName.toString() 17 | } else if (isArray(newName)) { 18 | elm.className = '' 19 | newName.forEach(v => { 20 | elm.classList.add(v) 21 | }) 22 | } else { 23 | // 所有不合法的值或者空值,都把 className 设为 '' 24 | elm.className = '' 25 | } 26 | } 27 | 28 | export const classModule = { 29 | create: updateClassName, 30 | update: updateClassName 31 | } 32 | export default classModule 33 | -------------------------------------------------------------------------------- /codes/vue/src/core/util/lang.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if a string starts with $ or _ 3 | */ 4 | export function isReserved(str) { 5 | const c = (str + '').charCodeAt(0) 6 | return c === 0x24 || c === 0x5F 7 | } 8 | 9 | /** 10 | * Define a property. 11 | */ 12 | export function def(obj, key, val, enumerable) { 13 | Object.defineProperty(obj, key, { 14 | value: val, 15 | enumerable: !!enumerable, 16 | writable: true, 17 | configurable: true 18 | }) 19 | } 20 | 21 | /** 22 | * Parse simple path. 23 | */ 24 | const bailRE = /[^\w.$]/ 25 | export function parsePath(path) { 26 | if (bailRE.test(path)) { 27 | return 28 | } 29 | const segments = path.split('.') 30 | return function (obj) { 31 | for (let i = 0; i < segments.length; i++) { 32 | if (!obj) return 33 | obj = obj[segments[i]] 34 | } 35 | return obj 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /codes/snabbdom/src/modules/props.js: -------------------------------------------------------------------------------- 1 | /** 2 | * props 模块:支持 vnode 使用 props 来操作其它属性。 3 | */ 4 | function filterKeys(obj) { 5 | return Object.keys(obj).filter(k => { 6 | return k !== 'style' && k !== 'id' && k !== 'class' 7 | }) 8 | } 9 | 10 | function updateProps(oldVnode, vnode) { 11 | let oldProps = oldVnode.data.props 12 | let props = vnode.data.props 13 | const elm = vnode.elm 14 | let key, cur, old 15 | 16 | if (!oldProps && !props) return 17 | if (oldProps === props) return 18 | oldProps = oldProps || {} 19 | props = props || {} 20 | 21 | filterKeys(oldProps).forEach(key => { 22 | if (!props[key]) { 23 | delete elm[key] 24 | } 25 | }) 26 | filterKeys(props).forEach(key => { 27 | cur = props[key] 28 | old = oldProps[key] 29 | if (old !== cur && (key !== 'value' || elm[key] !== cur)) { 30 | elm[key] = cur 31 | } 32 | }) 33 | } 34 | 35 | export const propsModule = {create: updateProps, update: updateProps} 36 | export default propsModule 37 | -------------------------------------------------------------------------------- /codes/didact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "didact", 3 | "version": "2.0.1", 4 | "description": "A didactic alternative to React.", 5 | "license": "MIT", 6 | "main": "lib/didact.umd.js", 7 | "module": "lib/didact.es.js", 8 | "files": [ 9 | "lib" 10 | ], 11 | "scripts": { 12 | "build": "rm -rf lib && tsc -P tsconfig.json", 13 | "test": "npm run build && ava", 14 | "lint": "tslint src/**/*.ts -t verbose" 15 | }, 16 | "keywords": [ 17 | "react" 18 | ], 19 | "devDependencies": { 20 | "@babel/core": "^7.0.0-beta.42", 21 | "@babel/plugin-transform-react-jsx": "^7.0.0-beta.42", 22 | "@babel/preset-env": "^7.0.0-beta.42", 23 | "@babel/register": "^7.0.0-beta.42", 24 | "ava": "^1.0.0-beta.3", 25 | "browser-env": "^3.2.5", 26 | "tslint": "^5.9.1", 27 | "typescript": "^2.7.2" 28 | }, 29 | "ava": { 30 | "require": [ 31 | "@babel/register" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /codes/snabbdom/src/h.js: -------------------------------------------------------------------------------- 1 | import { isArray, isPrimitive, flattenArray } from './utils' 2 | import vnode from './vnode' 3 | 4 | const hasOwnProperty = Object.prototype.hasOwnProperty 5 | const RESERVED_PROPS = { 6 | key: true, 7 | __self: true, 8 | __source: true 9 | } 10 | 11 | export default h 12 | 13 | function hasValidKey(config) { 14 | return config.key !== undefined 15 | } 16 | 17 | function h(type, config, ...children) { 18 | const props = {} 19 | 20 | let key = null 21 | 22 | // 获取 key,填充 props 对象 23 | if (config != null) { 24 | if (hasValidKey(config)) { 25 | key = '' + config.key 26 | } 27 | 28 | for (let propName in config) { 29 | if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS[propName]) { 30 | props[propName] = config[propName] 31 | } 32 | } 33 | } 34 | 35 | return vnode( 36 | type, 37 | key, 38 | props, 39 | flattenArray(children).map(c => { 40 | return isPrimitive(c) ? vnode(undefined, undefined, undefined, undefined, c) : c 41 | }) 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /codes/didact/src/component.ts: -------------------------------------------------------------------------------- 1 | import { IFiber, IProps, IState, IVNode } from './interface'; 2 | import { scheduleUpdate } from './reconciler'; 3 | 4 | /** 5 | * @name Component 6 | * @description 组件基类,定义了构造函数和 setState 7 | */ 8 | export class Component { 9 | public props: IProps; 10 | public state?: IState; 11 | // tslint:disable-next-line:variable-name 12 | public __fiber?: IFiber; 13 | constructor(props: IProps | null) { 14 | // 初始化 props 和 state 15 | this.props = props || {}; 16 | } 17 | // 当子类的实例调用 setState 时,触发更新计划 18 | public setState(partialState: any) { 19 | scheduleUpdate(this, partialState); 20 | } 21 | public render(): IVNode | IVNode[] | null | undefined { 22 | throw new Error('subclass must implement render method.'); 23 | } 24 | } 25 | 26 | /** 27 | * 创建组件实例 28 | * @param {Fiber} fiber 从fiber创建组件实例 29 | */ 30 | export function createInstance(fiber: IFiber) { 31 | const instance: Component = new (fiber.type as any)(fiber.props); 32 | instance.__fiber = fiber; 33 | return instance; 34 | } 35 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/render-list.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { isObject, isDef } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering v-for lists. 7 | */ 8 | export function renderList ( 9 | val , 10 | render 11 | 12 | 13 | 14 | 15 | ) { 16 | let ret , i, l, keys, key 17 | if (Array.isArray(val) || typeof val === 'string') { 18 | ret = new Array(val.length) 19 | for (i = 0, l = val.length; i < l; i++) { 20 | ret[i] = render(val[i], i) 21 | } 22 | } else if (typeof val === 'number') { 23 | ret = new Array(val) 24 | for (i = 0; i < val; i++) { 25 | ret[i] = render(i + 1, i) 26 | } 27 | } else if (isObject(val)) { 28 | keys = Object.keys(val) 29 | ret = new Array(keys.length) 30 | for (i = 0, l = keys.length; i < l; i++) { 31 | key = keys[i] 32 | ret[i] = render(val[key], key, i) 33 | } 34 | } 35 | if (isDef(ret)) { 36 | (ret )._isVList = true 37 | } 38 | return ret 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Creeper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codes/didact/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Hexacta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /codes/minipack/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ronen Amiel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/webpack-demo/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tania Rascia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/check-keycodes.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import config from 'core/config' 4 | import { hyphenate } from 'shared/util' 5 | 6 | function isKeyNotMatch (expect , actual ) { 7 | if (Array.isArray(expect)) { 8 | return expect.indexOf(actual) === -1 9 | } else { 10 | return expect !== actual 11 | } 12 | } 13 | 14 | /** 15 | * Runtime helper for checking keyCodes from config. 16 | * exposed as Vue.prototype._k 17 | * passing in eventKeyName as last argument separately for backwards compat 18 | */ 19 | export function checkKeyCodes ( 20 | eventKeyCode , 21 | key , 22 | builtInKeyCode , 23 | eventKeyName , 24 | builtInKeyName 25 | ) { 26 | const mappedKeyCode = config.keyCodes[key] || builtInKeyCode 27 | if (builtInKeyName && eventKeyName && !config.keyCodes[key]) { 28 | return isKeyNotMatch(builtInKeyName, eventKeyName) 29 | } else if (mappedKeyCode) { 30 | return isKeyNotMatch(mappedKeyCode, eventKeyCode) 31 | } else if (eventKeyName) { 32 | return hyphenate(eventKeyName) !== key 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /codes/vue/src/core/observer/array.js: -------------------------------------------------------------------------------- 1 | /* 2 | * not type checking this file because flow doesn't play well with 3 | * dynamically accessing methods on Array prototype 4 | */ 5 | 6 | import { def } from '../util/index' 7 | 8 | const arrayProto = Array.prototype 9 | export const arrayMethods = Object.create(arrayProto) 10 | 11 | const methodsToPatch = [ 12 | 'push', 13 | 'pop', 14 | 'shift', 15 | 'unshift', 16 | 'splice', 17 | 'sort', 18 | 'reverse' 19 | ] 20 | 21 | /** 22 | * Intercept mutating methods and emit events 23 | */ 24 | methodsToPatch.forEach(function (method) { 25 | // cache original method 26 | const original = arrayProto[method] 27 | def(arrayMethods, method, function mutator (...args) { 28 | const result = original.apply(this, args) 29 | const ob = this.__ob__ 30 | let inserted 31 | switch (method) { 32 | case 'push': 33 | case 'unshift': 34 | inserted = args 35 | break 36 | case 'splice': 37 | inserted = args.slice(2) 38 | break 39 | } 40 | if (inserted) ob.observeArray(inserted) 41 | // notify change 42 | ob.dep.notify() 43 | return result 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/index.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util' 4 | import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode' 5 | import { renderList } from './render-list' 6 | import { renderSlot } from './render-slot' 7 | import { resolveFilter } from './resolve-filter' 8 | import { checkKeyCodes } from './check-keycodes' 9 | import { bindObjectProps } from './bind-object-props' 10 | import { renderStatic, markOnce } from './render-static' 11 | import { bindObjectListeners } from './bind-object-listeners' 12 | import { resolveScopedSlots } from './resolve-slots' 13 | 14 | export function installRenderHelpers (target ) { 15 | target._o = markOnce 16 | target._n = toNumber 17 | target._s = toString 18 | target._l = renderList 19 | target._t = renderSlot 20 | target._q = looseEqual 21 | target._i = looseIndexOf 22 | target._m = renderStatic 23 | target._f = resolveFilter 24 | target._k = checkKeyCodes 25 | target._b = bindObjectProps 26 | target._v = createTextVNode 27 | target._e = createEmptyVNode 28 | target._u = resolveScopedSlots 29 | target._g = bindObjectListeners 30 | } 31 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/merge-hook.js: -------------------------------------------------------------------------------- 1 | import VNode from '../vnode' 2 | import { 3 | createFnInvoker 4 | } from './update-listeners' 5 | import { 6 | remove, 7 | isDef, 8 | isUndef, 9 | isTrue 10 | } from '../../../shared/util' 11 | 12 | export function mergeVNodeHook(def, hookKey, hook) { 13 | if (def instanceof VNode) { 14 | def = def.data.hook || (def.data.hook = {}) 15 | } 16 | let invoker 17 | const oldHook = def[hookKey] 18 | 19 | function wrappedHook() { 20 | hook.apply(this, arguments) 21 | // important: remove merged hook to ensure it's called only once 22 | // and prevent memory leak 23 | remove(invoker.fns, wrappedHook) 24 | } 25 | 26 | if (isUndef(oldHook)) { 27 | // no existing hook 28 | invoker = createFnInvoker([wrappedHook]) 29 | } else { 30 | /* istanbul ignore if */ 31 | if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { 32 | // already a merged invoker 33 | invoker = oldHook 34 | invoker.fns.push(wrappedHook) 35 | } else { 36 | // existing plain hook 37 | invoker = createFnInvoker([oldHook, wrappedHook]) 38 | } 39 | } 40 | 41 | invoker.merged = true 42 | def[hookKey] = invoker 43 | } 44 | -------------------------------------------------------------------------------- /codes/vue/src/core/global-api/assets.js: -------------------------------------------------------------------------------- 1 | import { 2 | ASSET_TYPES 3 | } from '../../shared/constants' 4 | import { 5 | isPlainObject, 6 | validateComponentName 7 | } from '../util/index' 8 | 9 | export function initAssetRegisters(Vue) { 10 | /** 11 | * Create asset registration methods. 12 | */ 13 | ASSET_TYPES.forEach(type => { 14 | Vue[type] = function ( 15 | id, 16 | definition 17 | ) { 18 | if (!definition) { 19 | return this.options[type + 's'][id] 20 | } else { 21 | /* istanbul ignore if */ 22 | if (process.env.NODE_ENV !== 'production' && type === 'component') { 23 | validateComponentName(id) 24 | } 25 | if (type === 'component' && isPlainObject(definition)) { 26 | definition.name = definition.name || id 27 | definition = this.options._base.extend(definition) 28 | } 29 | if (type === 'directive' && typeof definition === 'function') { 30 | definition = { 31 | bind: definition, 32 | update: definition 33 | } 34 | } 35 | this.options[type + 's'][id] = definition 36 | return definition 37 | } 38 | } 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /sites_and_articles.md: -------------------------------------------------------------------------------- 1 | 记录优秀的技术网站和文章。 2 | 3 | ### 文章 4 | 5 | 1. [通过源码解析 Node.js 中 cluster 模块的主要功能实现](https://cnodejs.org/topic/56e84480833b7c8a0492e20c) `node.js/cluster/source-code` 6 | 2. [Clearing up the Babel 6 Ecosystem](https://medium.com/@jcse/clearing-up-the-babel-6-ecosystem-c7678a314bf3#.f0xpc1nfa) `babel/babel-polyfill/babel-runtime` 7 | 3. [Node.js 启动方式:一道关于全局变量的题目引发的思考](https://xcoder.in/2015/11/26/a-js-problem-about-global/) `node.js/source-code/global variable` 8 | 4. [如何 hack Node.js 模块?](http://taobaofed.org/blog/2016/10/27/how-to-hack-nodejs-modules/) `node.js/source-code/module` 9 | 5. [Node.js定制REPL的妙用](https://cnodejs.org/topic/563735ed677332084c319d95) `node.js/repl` 10 | 6. [Node.js源码阅读笔记](https://cattail.me/tech/2014/10/16/nodejs-source-reading-note.html) `node.js/source-code` 11 | 7. [谷歌IO--V8之JavaScript性能](http://v8-io12.appspot.com/index.html) 12 | 8. [5 Easy Steps to Understanding JSON Web Tokens (JWT)](https://medium.com/vandium-software/5-easy-steps-to-understanding-json-web-tokens-jwt-1164c0adfcec) `jwt` *(简洁地介绍了jwt是什么,怎么生成,做什么。jwt:完全不负责数据的安全性,只用于校验用户的来源)* 13 | 14 | ### 网站 15 | 16 | - [alinode解读node源码:deep-into-node](https://yjhjstz.gitbooks.io/deep-into-node/) 17 | - [netflix技术博客](http://techblog.netflix.com/) 18 | -------------------------------------------------------------------------------- /codes/didact/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": [ 7 | "es2015", 8 | "dom" 9 | ], 10 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | "outDir": "./lib", /* Redirect output structure to the directory. */ 12 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 13 | /* Strict Type-Checking Options */ 14 | "strict": true, /* Enable all strict type-checking options. */ 15 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 16 | /* Additional Checks */ 17 | "noUnusedLocals": true, /* Report errors on unused locals. */ 18 | "noUnusedParameters": true /* Report errors on unused parameters. */ 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | "test" 23 | ] 24 | } -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/modules/ref.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { remove, isDef } from 'shared/util' 4 | 5 | export default { 6 | create (_ , vnode ) { 7 | registerRef(vnode) 8 | }, 9 | update (oldVnode , vnode ) { 10 | if (oldVnode.data.ref !== vnode.data.ref) { 11 | registerRef(oldVnode, true) 12 | registerRef(vnode) 13 | } 14 | }, 15 | destroy (vnode ) { 16 | registerRef(vnode, true) 17 | } 18 | } 19 | 20 | export function registerRef (vnode , isRemoval ) { 21 | const key = vnode.data.ref 22 | if (!isDef(key)) return 23 | 24 | const vm = vnode.context 25 | const ref = vnode.componentInstance || vnode.elm 26 | const refs = vm.$refs 27 | if (isRemoval) { 28 | if (Array.isArray(refs[key])) { 29 | remove(refs[key], ref) 30 | } else if (refs[key] === ref) { 31 | refs[key] = undefined 32 | } 33 | } else { 34 | if (vnode.data.refInFor) { 35 | if (!Array.isArray(refs[key])) { 36 | refs[key] = [ref] 37 | } else if (refs[key].indexOf(ref) < 0) { 38 | // $flow-disable-line 39 | refs[key].push(ref) 40 | } 41 | } else { 42 | refs[key] = ref 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/webpack-demo/config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const { merge } = require('webpack-merge') 3 | const common = require('./webpack.common.js') 4 | const paths = require('./paths') 5 | 6 | module.exports = merge(common, { 7 | // Set the mode to development or production 8 | mode: 'development', 9 | 10 | // Control how source maps are generated 11 | devtool: 'inline-source-map', 12 | 13 | // Spin up a server for quick development 14 | devServer: { 15 | historyApiFallback: true, 16 | contentBase: paths.build, 17 | open: true, 18 | compress: true, 19 | hot: true, 20 | port: 8080, 21 | }, 22 | 23 | module: { 24 | rules: [ 25 | // Styles: Inject CSS into the head with source maps 26 | { 27 | test: /\.(scss|css)$/, 28 | use: [ 29 | 'style-loader', 30 | {loader: 'css-loader', options: {sourceMap: true, importLoaders: 1, modules: true }}, 31 | {loader: 'postcss-loader', options: {sourceMap: true}}, 32 | {loader: 'sass-loader', options: {sourceMap: true}}, 33 | ], 34 | }, 35 | ] 36 | }, 37 | 38 | plugins: [ 39 | // Only update what has changed on hot reload 40 | new webpack.HotModuleReplacementPlugin(), 41 | ], 42 | }) 43 | -------------------------------------------------------------------------------- /codes/snabbdom/src/vnode.js: -------------------------------------------------------------------------------- 1 | // ⚠️ 通过 symbol 保证唯一性,用于检测是不是 vnode 2 | const VNODE_TYPE = Symbol('virtual-node') 3 | 4 | /** 5 | * 生成 vnode 6 | * @param {String} type 类型,如 'div' 7 | * @param {String} key key 8 | * @param {Object} data data,包括属性,事件等等 9 | * @param {Array} children 子 vnode 10 | * @param {String} text 文本 11 | * @param {Element} elm 对应的 dom 12 | * @return {Object} vnode 13 | */ 14 | function vnode(type, key, data, children, text, elm) { 15 | const element = { 16 | __type: VNODE_TYPE, 17 | type, key, data, children, text, elm 18 | } 19 | 20 | return element 21 | } 22 | 23 | /** 24 | * 校验是不是 vnode,主要检查 __type。 25 | * @param {Object} vnode 要检查的对象 26 | * @return {Boolean} 是则 true,否则 false 27 | */ 28 | function isVnode(vnode) { 29 | return vnode && vnode.__type === VNODE_TYPE 30 | } 31 | 32 | /** 33 | * 检查两个 vnode 是不是同一个:key 相同且 type 相同。 34 | * @param {Object} oldVnode 前一个 vnode 35 | * @param {Object} vnode 后一个 vnode 36 | * @return {Boolean} 是则 true,否则 false 37 | */ 38 | function isSameVnode(oldVnode, vnode) { 39 | return oldVnode.key === vnode.key && oldVnode.type === vnode.type 40 | } 41 | 42 | export default vnode 43 | export { 44 | isVnode, 45 | isSameVnode, 46 | VNODE_TYPE 47 | } 48 | -------------------------------------------------------------------------------- /codes/vue/src/core/observer/traverse.js: -------------------------------------------------------------------------------- 1 | import { 2 | _Set as Set, 3 | isObject 4 | } from '../util/index' 5 | 6 | import VNode from '../vdom/vnode' 7 | 8 | const seenObjects = new Set() 9 | 10 | /** 11 | * Recursively traverse an object to evoke all converted 12 | * getters, so that every nested property inside the object 13 | * is collected as a "deep" dependency. 14 | * 深度收集依赖, 通过 seenObjects 防止重复收集。 15 | */ 16 | export function traverse(val) { 17 | _traverse(val, seenObjects) 18 | seenObjects.clear() 19 | } 20 | 21 | // 递归遍历 val,深度收集依赖 22 | function _traverse(val, seen) { 23 | let i, keys 24 | const isA = Array.isArray(val) 25 | // 如果不是数组或对象,或者是 VNode,或者frozen,则不再处理。 26 | if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { 27 | return 28 | } 29 | if (val.__ob__) { 30 | const depId = val.__ob__.dep.id 31 | // 如果已经收集过,则不再重复处理了。 32 | if (seen.has(depId)) { 33 | return 34 | } 35 | seen.add(depId) 36 | } 37 | 38 | // 对数组的每个元素调用 _traverse 39 | if (isA) { 40 | i = val.length 41 | while (i--) _traverse(val[i], seen) 42 | } 43 | // 对子属性(val[keys[i]])访问,即调用 defineReactvie 定义的 getter,收集依赖 44 | else { 45 | keys = Object.keys(val) 46 | i = keys.length 47 | while (i--) _traverse(val[keys[i]], seen) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /codes/didact/test/03.reconciliation.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | /** @jsx createElement */ 4 | import { render, createElement } from '../lib'; 5 | 6 | test.beforeEach(t => { 7 | let root = document.getElementById('root'); 8 | if (!root) { 9 | root = document.createElement('div'); 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | } 13 | t.context.root = root; 14 | }); 15 | 16 | test('replace div to span', t => { 17 | const root = t.context.root; 18 | let element =
Foo
; 19 | render(element, root); 20 | t.is(root.innerHTML, '
Foo
'); 21 | const prevChild = root.firstElementChild; 22 | element = Foo; 23 | render(element, root); 24 | t.is(root.innerHTML, 'Foo'); 25 | const nextChild = root.firstElementChild; 26 | t.not(prevChild, nextChild); 27 | }); 28 | 29 | test('reuse div', t => { 30 | const root = t.context.root; 31 | let element =
Foo
; 32 | render(element, root); 33 | t.is(root.innerHTML, '
Foo
'); 34 | const prevChild = root.firstElementChild; 35 | element =
Bar
; 36 | render(element, root); 37 | t.is(root.innerHTML, '
Bar
'); 38 | const nextChild = root.firstElementChild; 39 | t.is(prevChild, nextChild); 40 | }); 41 | -------------------------------------------------------------------------------- /codes/didact/test/05.set-state.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | /** @jsx createElement */ 4 | import { render, createElement, Component } from '../lib'; 5 | 6 | test.beforeEach(t => { 7 | let root = document.getElementById('root'); 8 | if (!root) { 9 | root = document.createElement('div'); 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | } 13 | t.context.root = root; 14 | }); 15 | 16 | test('change state on click', t => { 17 | const root = t.context.root; 18 | class FooComponent extends Component { 19 | constructor(props) { 20 | super(props); 21 | this.state = { 22 | count: 0 23 | }; 24 | } 25 | 26 | handleClick() { 27 | this.setState({ 28 | count: this.state.count + 1 29 | }); 30 | } 31 | 32 | render() { 33 | return
this.handleClick()}>{this.state.count}
; 34 | } 35 | } 36 | render(, root); 37 | t.is(root.innerHTML, '
0
'); 38 | click(root.firstChild); 39 | t.is(root.innerHTML, '
1
'); 40 | }); 41 | 42 | function click(dom) { 43 | var evt = document.createEvent('MouseEvent'); 44 | evt.initEvent("click", false, true); 45 | dom.dispatchEvent(evt); 46 | } 47 | -------------------------------------------------------------------------------- /codes/vue/src/core/util/error.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { 3 | warn 4 | } from './debug' 5 | import { 6 | inBrowser, 7 | inWeex 8 | } from './env' 9 | 10 | export function handleError(err, vm, info) { 11 | if (vm) { 12 | let cur = vm 13 | while ((cur = cur.$parent)) { 14 | const hooks = cur.$options.errorCaptured 15 | if (hooks) { 16 | for (let i = 0; i < hooks.length; i++) { 17 | try { 18 | const capture = hooks[i].call(cur, err, vm, info) === false 19 | if (capture) return 20 | } catch (e) { 21 | globalHandleError(e, cur, 'errorCaptured hook') 22 | } 23 | } 24 | } 25 | } 26 | } 27 | globalHandleError(err, vm, info) 28 | } 29 | 30 | function globalHandleError(err, vm, info) { 31 | if (config.errorHandler) { 32 | try { 33 | return config.errorHandler.call(null, err, vm, info) 34 | } catch (e) { 35 | logError(e, null, 'config.errorHandler') 36 | } 37 | } 38 | logError(err, vm, info) 39 | } 40 | 41 | function logError(err, vm, info) { 42 | if (process.env.NODE_ENV !== 'production') { 43 | warn(`Error in ${info}: "${err.toString()}"`, vm) 44 | } 45 | /* istanbul ignore else */ 46 | if ((inBrowser || inWeex) && typeof console !== 'undefined') { 47 | console.error(err) 48 | } else { 49 | throw err 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /codes/vue/src/core/observer/dep.js: -------------------------------------------------------------------------------- 1 | import { 2 | remove 3 | } from '../util/index' 4 | 5 | let uid = 0 6 | 7 | /** 8 | * A dep is an observable that can have multiple 9 | * directives subscribing to it. 10 | * 一个 dep 就是一个 observable ,可以有多个指令订阅它。 11 | */ 12 | export default class Dep { 13 | constructor() { 14 | this.id = uid++ 15 | this.subs = [] 16 | } 17 | 18 | // 添加订阅者 19 | addSub(sub) { 20 | this.subs.push(sub) 21 | } 22 | 23 | // 删除订阅者 24 | removeSub(sub) { 25 | remove(this.subs, sub) 26 | } 27 | 28 | // 收集依赖(把自身添加到taget的依赖列表里) 29 | depend() { 30 | if (Dep.target) { 31 | Dep.target.addDep(this) 32 | } 33 | } 34 | 35 | // 通知订阅者 36 | notify() { 37 | // stabilize the subscriber list first 38 | // 复制当前订阅者列表(slice浅拷贝),防止当前的订阅者列表受 39 | // add/remove 影响。 40 | const subs = this.subs.slice() 41 | // 逐一通知订阅者更新 42 | for (let i = 0, l = subs.length; i < l; i++) { 43 | subs[i].update() 44 | } 45 | } 46 | } 47 | 48 | // the current target watcher being evaluated. 49 | // this is globally unique because there could be only one 50 | // watcher being evaluated at any time. 51 | Dep.target = null 52 | const targetStack = [] 53 | 54 | export function pushTarget(_target) { 55 | if (Dep.target) targetStack.push(Dep.target) 56 | Dep.target = _target 57 | } 58 | 59 | export function popTarget() { 60 | Dep.target = targetStack.pop() 61 | } 62 | -------------------------------------------------------------------------------- /codes/didact/test/01.render-jsx-dom-elements.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | /** @jsx createElement */ 4 | import { render, createElement } from '../lib'; 5 | 6 | test.beforeEach(t => { 7 | let root = document.getElementById('root'); 8 | if (!root) { 9 | root = document.createElement('div'); 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | } 13 | t.context.root = root; 14 | }); 15 | 16 | test('render jsx div', t => { 17 | const root = t.context.root; 18 | const element =
; 19 | render(element, root); 20 | t.is(root.innerHTML, '
'); 21 | }); 22 | 23 | test('render div with children', t => { 24 | const root = t.context.root; 25 | const element = ( 26 |
27 | 28 | 29 |
30 | ); 31 | render(element, root); 32 | t.is(root.innerHTML, '
'); 33 | }); 34 | 35 | test('render div with props', t => { 36 | const root = t.context.root; 37 | const element =
; 38 | render(element, root); 39 | t.is(root.innerHTML, '
'); 40 | }); 41 | 42 | test('render span with text child', t => { 43 | const root = t.context.root; 44 | const element = Foo; 45 | render(element, root); 46 | t.is(root.innerHTML, 'Foo'); 47 | }); 48 | -------------------------------------------------------------------------------- /codes/didact/test/04.components.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | /** @jsx createElement */ 4 | import { render, createElement, Component } from '../lib'; 5 | 6 | test.beforeEach(t => { 7 | let root = document.getElementById('root'); 8 | if (!root) { 9 | root = document.createElement('div'); 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | } 13 | t.context.root = root; 14 | }); 15 | 16 | test('render component', t => { 17 | const root = t.context.root; 18 | class FooComponent extends Component { 19 | render() { 20 | return ( 21 |
22 | 23 | 24 |
25 | ); 26 | } 27 | } 28 | render(, root); 29 | t.is(root.innerHTML, '
'); 30 | }); 31 | 32 | test('render component with props', t => { 33 | const root = t.context.root; 34 | class FooComponent extends Component { 35 | render() { 36 | return ( 37 |
38 | {this.props.name} 39 | 40 |
41 | ); 42 | } 43 | } 44 | render(, root); 45 | t.is(root.innerHTML, ''); 46 | }); 47 | -------------------------------------------------------------------------------- /codes/snabbdom/demo/app.js: -------------------------------------------------------------------------------- 1 | const patch = snabbdom.patch 2 | const h = snabbdom.h 3 | 4 | const container = document.getElementById('container') 5 | 6 | const vnode = h('div', {id: 'container'}, [ 7 | h('span', {style: {fontWeight: 'bold'}}, 'This is bold'), 8 | ' and this is just normal text', 9 | h('a', { 10 | props: {href: '/foo'} 11 | }, 'I\'ll take you places!') 12 | ]) 13 | 14 | console.log(vnode) 15 | patch(container, vnode) 16 | 17 | const newVnode = h('div', {id: 'container'}, [ 18 | h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'), 19 | ' and this is still just normal text', 20 | h('a', { 21 | props: {href: '/bar'} 22 | }, 'I\'ll take you places!') 23 | ]) 24 | setTimeout(() => { 25 | patch(vnode, newVnode) 26 | }, 7000) 27 | 28 | 29 | const footer = document.getElementById('footer') 30 | 31 | function getList(count) { 32 | return h( 33 | 'ul', 34 | { 35 | id: 'footer', 36 | className: 'hello hi', 37 | style: { 38 | color: '#666' 39 | } 40 | }, 41 | Array.apply(null, {length: count}) 42 | .map((v, i) => i + 1) 43 | .map(n => h( 44 | 'li', 45 | { 46 | className: 'item', 47 | key: n 48 | }, 49 | `number is ${n}` 50 | )) 51 | ) 52 | } 53 | const sVnode = getList(3) 54 | patch(footer, sVnode) 55 | 56 | setTimeout(() => { 57 | patch(sVnode, getList(10)) 58 | }, 4000) 59 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/render-slot.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import { extend, warn, isObject } from 'core/util/index' 4 | 5 | /** 6 | * Runtime helper for rendering 7 | */ 8 | export function renderSlot ( 9 | name , 10 | fallback , 11 | props , 12 | bindObject 13 | ) { 14 | const scopedSlotFn = this.$scopedSlots[name] 15 | let nodes 16 | if (scopedSlotFn) { // scoped slot 17 | props = props || {} 18 | if (bindObject) { 19 | if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) { 20 | warn( 21 | 'slot v-bind without argument expects an Object', 22 | this 23 | ) 24 | } 25 | props = extend(extend({}, bindObject), props) 26 | } 27 | nodes = scopedSlotFn(props) || fallback 28 | } else { 29 | const slotNodes = this.$slots[name] 30 | // warn duplicate slot usage 31 | if (slotNodes) { 32 | if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) { 33 | warn( 34 | `Duplicate presence of slot "${name}" found in the same render tree ` + 35 | `- this will likely cause render errors.`, 36 | this 37 | ) 38 | } 39 | slotNodes._rendered = true 40 | } 41 | nodes = slotNodes || fallback 42 | } 43 | 44 | const target = props && props.slot 45 | if (target) { 46 | return this.$createElement('template', { slot: target }, nodes) 47 | } else { 48 | return nodes 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/webpack-demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": ["dist", "node_modules"], 3 | "rules": { 4 | "max-len": [ 5 | "error", 6 | { 7 | "ignoreUrls": true, 8 | "code": 80 9 | } 10 | ], 11 | "prefer-template": "off", 12 | "indent": ["error", 2], 13 | "object-curly-spacing": ["error", "always"], 14 | "no-multiple-empty-lines": [ 15 | "error", 16 | { 17 | "max": 1, 18 | "maxEOF": 1 19 | } 20 | ], 21 | "no-var": "error", 22 | "one-var": ["error", "never"], 23 | "camelcase": "error", 24 | "no-multi-assign": "error", 25 | "quotes": ["error", "single"], 26 | "no-array-constructor": "error", 27 | "no-new-object": "error", 28 | "no-new-wrappers": "error", 29 | "no-nested-ternary": "error", 30 | "no-console": [ 31 | "error", 32 | { 33 | "allow": ["error"] 34 | } 35 | ], 36 | "no-template-curly-in-string": "error", 37 | "no-self-compare": "error", 38 | "func-names": ["error", "as-needed"], 39 | "semi": [2, "never"], 40 | "import/no-extraneous-dependencies": ["off", { "devDependencies": false }] 41 | }, 42 | "env": { 43 | "browser": true, 44 | "es6": true 45 | }, 46 | "extends": ["eslint:recommended", "airbnb-base", "prettier"], 47 | "globals": { 48 | "Atomics": "readonly", 49 | "SharedArrayBuffer": "readonly" 50 | }, 51 | "parserOptions": { 52 | "ecmaVersion": 11, 53 | "sourceType": "module" 54 | }, 55 | "plugins": ["prettier"], 56 | "settings": { "import/resolver": "webpack" } 57 | } 58 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/bind-object-props.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | import config from 'core/config' 4 | 5 | import { 6 | warn, 7 | isObject, 8 | toObject, 9 | isReservedAttribute 10 | } from 'core/util/index' 11 | 12 | /** 13 | * Runtime helper for merging v-bind="object" into a VNode's data. 14 | */ 15 | export function bindObjectProps ( 16 | data , 17 | tag , 18 | value , 19 | asProp , 20 | isSync 21 | ) { 22 | if (value) { 23 | if (!isObject(value)) { 24 | process.env.NODE_ENV !== 'production' && warn( 25 | 'v-bind without argument expects an Object or Array value', 26 | this 27 | ) 28 | } else { 29 | if (Array.isArray(value)) { 30 | value = toObject(value) 31 | } 32 | let hash 33 | for (const key in value) { 34 | if ( 35 | key === 'class' || 36 | key === 'style' || 37 | isReservedAttribute(key) 38 | ) { 39 | hash = data 40 | } else { 41 | const type = data.attrs && data.attrs.type 42 | hash = asProp || config.mustUseProp(tag, type, key) 43 | ? data.domProps || (data.domProps = {}) 44 | : data.attrs || (data.attrs = {}) 45 | } 46 | if (!(key in hash)) { 47 | hash[key] = value[key] 48 | 49 | if (isSync) { 50 | const on = data.on || (data.on = {}) 51 | on[`update:${key}`] = function ($event) { 52 | value[key] = $event 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | return data 60 | } 61 | -------------------------------------------------------------------------------- /examples/webpack-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-boilerplate", 3 | "version": "2.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "cross-env NODE_ENV=development webpack serve --config config/webpack.dev.js", 8 | "build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js", 9 | "lint": "eslint . src config || true" 10 | }, 11 | "keywords": [ 12 | "webpack", 13 | "webpack 5", 14 | "webpack boilerplate" 15 | ], 16 | "devDependencies": { 17 | "@babel/core": "^7.12.1", 18 | "@babel/plugin-proposal-class-properties": "^7.12.1", 19 | "@babel/preset-env": "^7.12.1", 20 | "babel-loader": "^8.1.0", 21 | "clean-webpack-plugin": "^3.0.0", 22 | "copy-webpack-plugin": "^6.2.1", 23 | "cross-env": "^7.0.2", 24 | "css-loader": "^5.0.0", 25 | "css-minimizer-webpack-plugin": "^1.1.5", 26 | "eslint": "^7.12.1", 27 | "eslint-config-airbnb-base": "^14.2.0", 28 | "eslint-config-prettier": "^6.15.0", 29 | "eslint-import-resolver-webpack": "^0.13.0", 30 | "eslint-plugin-import": "^2.22.1", 31 | "eslint-plugin-prettier": "^3.1.4", 32 | "eslint-webpack-plugin": "^2.1.0", 33 | "html-webpack-plugin": "^5.0.0-alpha.7", 34 | "mini-css-extract-plugin": "^1.0.0", 35 | "node-sass": "^4.14.1", 36 | "postcss-loader": "^4.0.4", 37 | "postcss-preset-env": "^6.7.0", 38 | "prettier": "^2.1.2", 39 | "prettier-webpack-plugin": "^1.2.0", 40 | "sass-loader": "^10.0.3", 41 | "style-loader": "^2.0.0", 42 | "webpack": "^5.1.3", 43 | "webpack-cli": "^4.0.0", 44 | "webpack-dev-server": "^3.11.0", 45 | "webpack-merge": "^5.2.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/render-static.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | /** 4 | * Runtime helper for rendering static trees. 5 | */ 6 | export function renderStatic ( 7 | index , 8 | isInFor 9 | ) { 10 | const cached = this._staticTrees || (this._staticTrees = []) 11 | let tree = cached[index] 12 | // if has already-rendered static tree and not inside v-for, 13 | // we can reuse the same tree. 14 | if (tree && !isInFor) { 15 | return tree 16 | } 17 | // otherwise, render a fresh tree. 18 | tree = cached[index] = this.$options.staticRenderFns[index].call( 19 | this._renderProxy, 20 | null, 21 | this // for render fns generated for functional component templates 22 | ) 23 | markStatic(tree, `__static__${index}`, false) 24 | return tree 25 | } 26 | 27 | /** 28 | * Runtime helper for v-once. 29 | * Effectively it means marking the node as static with a unique key. 30 | */ 31 | export function markOnce ( 32 | tree , 33 | index , 34 | key 35 | ) { 36 | markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true) 37 | return tree 38 | } 39 | 40 | function markStatic ( 41 | tree , 42 | key , 43 | isOnce 44 | ) { 45 | if (Array.isArray(tree)) { 46 | for (let i = 0; i < tree.length; i++) { 47 | if (tree[i] && typeof tree[i] !== 'string') { 48 | markStaticNode(tree[i], `${key}_${i}`, isOnce) 49 | } 50 | } 51 | } else { 52 | markStaticNode(tree, key, isOnce) 53 | } 54 | } 55 | 56 | function markStaticNode (node, key, isOnce) { 57 | node.isStatic = true 58 | node.key = key 59 | node.isOnce = isOnce 60 | } 61 | -------------------------------------------------------------------------------- /codes/snabbdom/src/domApi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DOM 操作 API 3 | * 包括元素 创建/删除/插入/... 等等 4 | */ 5 | 6 | function createElement(tagName) { 7 | return document.createElement(tagName) 8 | } 9 | 10 | function createElementNS(namespaceURI, qualifiedName) { 11 | return document.createElementNS(namespaceURI, qualifiedName) 12 | } 13 | 14 | function createTextNode(text) { 15 | return document.createTextNode(text) 16 | } 17 | 18 | function createComment(text) { 19 | return document.createComment(text) 20 | } 21 | 22 | function insertBefore(parentNode, newNode, referenceNode) { 23 | parentNode.insertBefore(newNode, referenceNode) 24 | } 25 | 26 | function removeChild(node, child) { 27 | node.removeChild(child) 28 | } 29 | 30 | function appendChild(node, child) { 31 | node.appendChild(child) 32 | } 33 | 34 | function parentNode(node) { 35 | return node.parentNode 36 | } 37 | 38 | function nextSibling(node) { 39 | return node.nextSibling 40 | } 41 | 42 | function tagName(elm) { 43 | return elm.tagName 44 | } 45 | 46 | function setTextContent(node, text) { 47 | node.textContent = text 48 | } 49 | 50 | function getTextContent(node) { 51 | return node.textContent 52 | } 53 | 54 | function isElement(node) { 55 | return node.nodeType === 1 56 | } 57 | 58 | function isText(node) { 59 | return node.nodeType === 3 60 | } 61 | 62 | function isComment(node) { 63 | return node.nodeType === 8 64 | } 65 | 66 | export const htmlDomApi = { 67 | createElement, 68 | createElementNS, 69 | createTextNode, 70 | createComment, 71 | insertBefore, 72 | removeChild, 73 | appendChild, 74 | parentNode, 75 | nextSibling, 76 | tagName, 77 | setTextContent, 78 | getTextContent, 79 | isElement, 80 | isText, 81 | isComment, 82 | } 83 | 84 | export default htmlDomApi 85 | -------------------------------------------------------------------------------- /codes/snabbdom/src/modules/style.js: -------------------------------------------------------------------------------- 1 | /** 2 | * style 模块:支持 vnode 使用 style 来操作内连样式。 3 | */ 4 | 5 | const raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout 6 | 7 | const nextFrame = function(fn) { 8 | raf(function() { raf(fn) }) 9 | } 10 | 11 | function setNextFrame(obj, prop, val) { 12 | nextFrame(function() { 13 | obj[prop] = val 14 | }) 15 | } 16 | 17 | function updateStyle(oldVnode, vnode) { 18 | let oldStyle = oldVnode.data.style 19 | let style = vnode.data.style 20 | const elm = vnode.elm 21 | let name, cur 22 | 23 | if (!oldStyle && !style) return 24 | if (oldStyle === style) return 25 | oldStyle = oldStyle || {} 26 | style = style || {} 27 | 28 | let oldHasDel = 'delayed' in oldStyle 29 | 30 | // 删除 style 中没有而 oldStyle 中有的属性 31 | for (name in oldStyle) { 32 | if (!style[name]) { 33 | if (name[0] === '-' && name[1] === '-') { 34 | elm.style.removeProperty(name) 35 | } else { 36 | elm.style[name] = '' 37 | } 38 | } 39 | } 40 | // 更新 style 41 | for (name in style) { 42 | cur = style[name] 43 | // delayed 44 | if (name === 'delayed') { 45 | for (name in style.delayed) { 46 | cur = style.delayed[name] 47 | if (!oldHasDel || cur !== oldStyle.delayed[name]) { 48 | setNextFrame(elm.style, name, cur) 49 | } 50 | } 51 | } 52 | // 普通 53 | else if (name !== 'remove' && cur !== oldStyle[name]) { 54 | if (name[0] === '-' && name[1] === '-') { 55 | elm.style.setProperty(name, cur) 56 | } else { 57 | elm.style[name] = cur 58 | } 59 | } 60 | } 61 | } 62 | 63 | export const styleModule = { 64 | create: updateStyle, 65 | update: updateStyle 66 | } 67 | export default styleModule 68 | -------------------------------------------------------------------------------- /codes/vue/src/core/global-api/index.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { 3 | initUse 4 | } from './use' 5 | import { 6 | initMixin 7 | } from './mixin' 8 | import { 9 | initExtend 10 | } from './extend' 11 | import { 12 | initAssetRegisters 13 | } from './assets' 14 | import { 15 | set, 16 | del 17 | } from '../observer/index' 18 | import { 19 | ASSET_TYPES 20 | } from '../../shared/constants' 21 | import builtInComponents from '../components/index' 22 | 23 | import { 24 | warn, 25 | extend, 26 | nextTick, 27 | mergeOptions, 28 | defineReactive 29 | } from '../util/index' 30 | 31 | export function initGlobalAPI(Vue) { 32 | // config 33 | const configDef = {} 34 | configDef.get = () => config 35 | if (process.env.NODE_ENV !== 'production') { 36 | configDef.set = () => { 37 | warn( 38 | 'Do not replace the Vue.config object, set individual fields instead.' 39 | ) 40 | } 41 | } 42 | Object.defineProperty(Vue, 'config', configDef) 43 | 44 | // exposed util methods. 45 | // NOTE: these are not considered part of the public API - avoid relying on 46 | // them unless you are aware of the risk. 47 | Vue.util = { 48 | warn, 49 | extend, 50 | mergeOptions, 51 | defineReactive 52 | } 53 | 54 | Vue.set = set 55 | Vue.delete = del 56 | Vue.nextTick = nextTick 57 | 58 | Vue.options = Object.create(null) 59 | ASSET_TYPES.forEach(type => { 60 | Vue.options[type + 's'] = Object.create(null) 61 | }) 62 | 63 | // this is used to identify the "base" constructor to extend all plain-object 64 | // components with in Weex's multi-instance scenarios. 65 | Vue.options._base = Vue 66 | 67 | extend(Vue.options.components, builtInComponents) 68 | 69 | initUse(Vue) 70 | initMixin(Vue) 71 | initExtend(Vue) 72 | initAssetRegisters(Vue) 73 | } 74 | -------------------------------------------------------------------------------- /examples/webpack-demo/config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const paths = require('./paths') 2 | const { merge } = require('webpack-merge') 3 | const common = require('./webpack.common.js') 4 | 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') 7 | 8 | module.exports = merge(common, { 9 | mode: 'production', 10 | devtool: false, 11 | output: { 12 | path: paths.build, 13 | publicPath: '/', 14 | filename: 'js/[name].[contenthash].bundle.js', 15 | chunkLoadingGlobal: 'webpackChunkwebpack', 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.(scss|css)$/, 21 | use: [ 22 | MiniCssExtractPlugin.loader, 23 | { 24 | loader: 'css-loader', 25 | options: { 26 | importLoaders: 2, 27 | sourceMap: false, 28 | modules: true, 29 | }, 30 | }, 31 | 'postcss-loader', 32 | 'sass-loader', 33 | ], 34 | }, 35 | ], 36 | }, 37 | plugins: [ 38 | // Extracts CSS into separate files 39 | // Note: style-loader is for development, MiniCssExtractPlugin is for production 40 | new MiniCssExtractPlugin({ 41 | filename: 'styles/[name].[contenthash].css', 42 | chunkFilename: '[id].css', 43 | }), 44 | ], 45 | optimization: { 46 | minimize: false, 47 | minimizer: [new CssMinimizerPlugin(), "..."], 48 | // Once your build outputs multiple chunks, this option will ensure they share the webpack runtime 49 | // instead of having their own. This also helps with long-term caching, since the chunks will only 50 | // change when actual code changes, not the webpack runtime. 51 | runtimeChunk: { 52 | name: 'runtime', 53 | }, 54 | chunkIds: 'named', 55 | }, 56 | performance: { 57 | hints: false, 58 | maxEntrypointSize: 512000, 59 | maxAssetSize: 512000, 60 | }, 61 | }) 62 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/render-helpers/resolve-slots.js: -------------------------------------------------------------------------------- 1 | /* */ 2 | 3 | 4 | 5 | /** 6 | * Runtime helper for resolving raw children VNodes into a slot object. 7 | */ 8 | export function resolveSlots ( 9 | children , 10 | context 11 | ) { 12 | const slots = {} 13 | if (!children) { 14 | return slots 15 | } 16 | for (let i = 0, l = children.length; i < l; i++) { 17 | const child = children[i] 18 | const data = child.data 19 | // remove slot attribute if the node is resolved as a Vue slot node 20 | if (data && data.attrs && data.attrs.slot) { 21 | delete data.attrs.slot 22 | } 23 | // named slots should only be respected if the vnode was rendered in the 24 | // same context. 25 | if ((child.context === context || child.fnContext === context) && 26 | data && data.slot != null 27 | ) { 28 | const name = data.slot 29 | const slot = (slots[name] || (slots[name] = [])) 30 | if (child.tag === 'template') { 31 | slot.push.apply(slot, child.children || []) 32 | } else { 33 | slot.push(child) 34 | } 35 | } else { 36 | (slots.default || (slots.default = [])).push(child) 37 | } 38 | } 39 | // ignore slots that contains only whitespace 40 | for (const name in slots) { 41 | if (slots[name].every(isWhitespace)) { 42 | delete slots[name] 43 | } 44 | } 45 | return slots 46 | } 47 | 48 | function isWhitespace (node ) { 49 | return (node.isComment && !node.asyncFactory) || node.text === ' ' 50 | } 51 | 52 | export function resolveScopedSlots ( 53 | fns , // see flow/vnode 54 | res 55 | ) { 56 | res = res || {} 57 | for (let i = 0; i < fns.length; i++) { 58 | if (Array.isArray(fns[i])) { 59 | resolveScopedSlots(fns[i], res) 60 | } else { 61 | res[fns[i].key] = fns[i].fn 62 | } 63 | } 64 | return res 65 | } 66 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/extract-props.js: -------------------------------------------------------------------------------- 1 | import { 2 | tip, 3 | hasOwn, 4 | isDef, 5 | isUndef, 6 | hyphenate, 7 | formatComponentName 8 | } from '../../util/index' 9 | 10 | export function extractPropsFromVNodeData( 11 | data, 12 | Ctor, 13 | tag 14 | ) { 15 | // we are only extracting raw values here. 16 | // validation and default values are handled in the child 17 | // component itself. 18 | const propOptions = Ctor.options.props 19 | if (isUndef(propOptions)) { 20 | return 21 | } 22 | const res = {} 23 | const { 24 | attrs, 25 | props 26 | } = data 27 | if (isDef(attrs) || isDef(props)) { 28 | for (const key in propOptions) { 29 | const altKey = hyphenate(key) 30 | if (process.env.NODE_ENV !== 'production') { 31 | const keyInLowerCase = key.toLowerCase() 32 | if ( 33 | key !== keyInLowerCase && 34 | attrs && hasOwn(attrs, keyInLowerCase) 35 | ) { 36 | tip( 37 | `Prop "${keyInLowerCase}" is passed to component ` + 38 | `${formatComponentName(tag || Ctor)}, but the declared prop name is` + 39 | ` "${key}". ` + 40 | `Note that HTML attributes are case-insensitive and camelCased ` + 41 | `props need to use their kebab-case equivalents when using in-DOM ` + 42 | `templates. You should probably use "${altKey}" instead of "${key}".` 43 | ) 44 | } 45 | } 46 | checkProp(res, props, key, altKey, true) || 47 | checkProp(res, attrs, key, altKey, false) 48 | } 49 | } 50 | return res 51 | } 52 | 53 | function checkProp( 54 | res, 55 | hash, 56 | key, 57 | altKey, 58 | preserve 59 | ) { 60 | if (isDef(hash)) { 61 | if (hasOwn(hash, key)) { 62 | res[key] = hash[key] 63 | if (!preserve) { 64 | delete hash[key] 65 | } 66 | return true 67 | } else if (hasOwn(hash, altKey)) { 68 | res[key] = hash[altKey] 69 | if (!preserve) { 70 | delete hash[altKey] 71 | } 72 | return true 73 | } 74 | } 75 | return false 76 | } 77 | -------------------------------------------------------------------------------- /codes/didact/src/interface.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component'; 2 | 3 | // Fiber 标签 4 | export enum ITag { 5 | HOST_COMPONENT = 'host', 6 | CLASS_COMPONENT = 'class', 7 | HOST_ROOT = 'root', 8 | } 9 | 10 | // Effect 标签 11 | export enum Effect { 12 | PLACEMENT = 1, 13 | DELETION = 2, 14 | UPDATE = 3, 15 | } 16 | 17 | export interface IdleDeadline { 18 | didTimeout: boolean; 19 | timeRemaining(): number; 20 | } 21 | 22 | export type IdleRequestCallback = (deadline: IdleDeadline) => any; 23 | 24 | export type ComponentType = string | (() => object); 25 | 26 | export interface IState { 27 | [key: string]: any; 28 | } 29 | 30 | export interface IVNode { 31 | type: ComponentType; 32 | props: { 33 | children?: IVNode[], 34 | [key: string]: any, 35 | }; 36 | } 37 | 38 | export interface IProps { 39 | children?: IVNode[]; 40 | style?: object; 41 | [key: string]: any; 42 | } 43 | 44 | export interface IFiber { 45 | tag: ITag; 46 | type?: ComponentType; 47 | 48 | // parent/child/sibling 用于构建 fiber tree,对应相应的组件树。 49 | parent?: IFiber | null; 50 | child?: IFiber | null; 51 | sibling?: IFiber | null; 52 | 53 | // 大多数时候,我们有2棵fiber树: 54 | // 1. 一棵对应已经渲染到DOM的,我们称之为 current tree / old tree; 55 | // 2. 一棵是我们正在创建的,对应新的更新(setState() 或者 React.render()),叫 work-in-progress tree。 56 | // ⚠️ work-in-progress tree 不和 old tree 共享任何 fiber;一旦 work-in-progress tree 创建 57 | // 完成并完成需要的 DOM 更新,work-in-progress tree 即变成 old tree 。 58 | // alternate 用于 work-in-progress fiber 链接/指向(link)到它们对应的 old tree 上的 fiber。 59 | // fiber 和它的 alternate 共享 tag, type 和 stateNode。 60 | alternate?: IFiber | null; 61 | 62 | // 指向组件实例的引用,可以是 DOM element 或者 Class Component 的实例 63 | stateNode?: Element | Component; 64 | 65 | props: IProps; 66 | partialState?: IState | null; 67 | effectTag?: Effect; 68 | effects?: IFiber[]; 69 | } 70 | 71 | export interface IUpdate { 72 | from: ITag; 73 | dom?: HTMLElement; 74 | instance?: Component; 75 | newProps?: IProps; 76 | partialState?: IState | null; 77 | } 78 | -------------------------------------------------------------------------------- /codes/vue/src/core/config.js: -------------------------------------------------------------------------------- 1 | import { 2 | no, 3 | noop, 4 | identity 5 | } from '../shared/util' 6 | 7 | import { 8 | LIFECYCLE_HOOKS 9 | } from '../shared/constants' 10 | 11 | export default ({ 12 | /** 13 | * Option merge strategies (used in core/util/options) 14 | */ 15 | // $flow-disable-line 16 | optionMergeStrategies: Object.create(null), 17 | 18 | /** 19 | * Whether to suppress warnings. 20 | */ 21 | silent: false, 22 | 23 | /** 24 | * Show production mode tip message on boot? 25 | */ 26 | productionTip: process.env.NODE_ENV !== 'production', 27 | 28 | /** 29 | * Whether to enable devtools 30 | */ 31 | devtools: process.env.NODE_ENV !== 'production', 32 | 33 | /** 34 | * Whether to record perf 35 | */ 36 | performance: false, 37 | 38 | /** 39 | * Error handler for watcher errors 40 | */ 41 | errorHandler: null, 42 | 43 | /** 44 | * Warn handler for watcher warns 45 | */ 46 | warnHandler: null, 47 | 48 | /** 49 | * Ignore certain custom elements 50 | */ 51 | ignoredElements: [], 52 | 53 | /** 54 | * Custom user key aliases for v-on 55 | */ 56 | // $flow-disable-line 57 | keyCodes: Object.create(null), 58 | 59 | /** 60 | * Check if a tag is reserved so that it cannot be registered as a 61 | * component. This is platform-dependent and may be overwritten. 62 | */ 63 | isReservedTag: no, 64 | 65 | /** 66 | * Check if an attribute is reserved so that it cannot be used as a component 67 | * prop. This is platform-dependent and may be overwritten. 68 | */ 69 | isReservedAttr: no, 70 | 71 | /** 72 | * Check if a tag is an unknown element. 73 | * Platform-dependent. 74 | */ 75 | isUnknownElement: no, 76 | 77 | /** 78 | * Get the namespace of an element 79 | */ 80 | getTagNamespace: noop, 81 | 82 | /** 83 | * Parse the real tag name for the specific platform. 84 | */ 85 | parsePlatformTagName: identity, 86 | 87 | /** 88 | * Check if an attribute must be bound using property, e.g. value 89 | * Platform-dependent. 90 | */ 91 | mustUseProp: no, 92 | 93 | /** 94 | * Exposed for legacy reasons 95 | */ 96 | _lifecycleHooks: LIFECYCLE_HOOKS 97 | }) 98 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/update-listeners.js: -------------------------------------------------------------------------------- 1 | import { 2 | warn 3 | } from '../../util/index' 4 | import { 5 | cached, 6 | isUndef, 7 | isPlainObject 8 | } from '../../../shared/util' 9 | 10 | const normalizeEvent = cached((name) => { 11 | const passive = name.charAt(0) === '&' 12 | name = passive ? name.slice(1) : name 13 | const once = name.charAt(0) === '~' // Prefixed last, checked first 14 | name = once ? name.slice(1) : name 15 | const capture = name.charAt(0) === '!' 16 | name = capture ? name.slice(1) : name 17 | return { 18 | name, 19 | once, 20 | capture, 21 | passive 22 | } 23 | }) 24 | 25 | export function createFnInvoker(fns) { 26 | function invoker() { 27 | const fns = invoker.fns 28 | if (Array.isArray(fns)) { 29 | const cloned = fns.slice() 30 | for (let i = 0; i < cloned.length; i++) { 31 | cloned[i].apply(null, arguments) 32 | } 33 | } else { 34 | // return handler return value for single handlers 35 | return fns.apply(null, arguments) 36 | } 37 | } 38 | invoker.fns = fns 39 | return invoker 40 | } 41 | 42 | export function updateListeners( 43 | on, 44 | oldOn, 45 | add, 46 | remove, 47 | vm 48 | ) { 49 | let name, def, cur, old, event 50 | for (name in on) { 51 | def = cur = on[name] 52 | old = oldOn[name] 53 | event = normalizeEvent(name) 54 | /* istanbul ignore if */ 55 | if (__WEEX__ && isPlainObject(def)) { 56 | cur = def.handler 57 | event.params = def.params 58 | } 59 | if (isUndef(cur)) { 60 | process.env.NODE_ENV !== 'production' && warn( 61 | `Invalid handler for event "${event.name}": got ` + String(cur), 62 | vm 63 | ) 64 | } else if (isUndef(old)) { 65 | if (isUndef(cur.fns)) { 66 | cur = on[name] = createFnInvoker(cur) 67 | } 68 | add(event.name, cur, event.once, event.capture, event.passive, event.params) 69 | } else if (cur !== old) { 70 | old.fns = cur 71 | on[name] = old 72 | } 73 | } 74 | for (name in oldOn) { 75 | if (isUndef(on[name])) { 76 | event = normalizeEvent(name) 77 | remove(event.name, oldOn[name], event.capture) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /codes/snabbdom/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Creeper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | The MIT License (MIT) 25 | 26 | Copyright (c) 2015 Simon Friis Vindum 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in all 36 | copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 44 | SOFTWARE. -------------------------------------------------------------------------------- /examples/webpack-demo/config/webpack.common.js: -------------------------------------------------------------------------------- 1 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 2 | const CopyWebpackPlugin = require('copy-webpack-plugin') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const PrettierPlugin = require('prettier-webpack-plugin') 5 | const ESLintPlugin = require('eslint-webpack-plugin') 6 | const paths = require('./paths') 7 | 8 | module.exports = { 9 | // Where webpack looks to start building the bundle 10 | entry: [paths.src + '/index.js'], 11 | 12 | // Where webpack outputs the assets and bundles 13 | output: { 14 | path: paths.build, 15 | filename: '[name].bundle.js', 16 | publicPath: '/', 17 | }, 18 | 19 | // Customize the webpack build process 20 | plugins: [ 21 | // Removes/cleans build folders and unused assets when rebuilding 22 | new CleanWebpackPlugin(), 23 | 24 | // Copies files from target to destination folder 25 | new CopyWebpackPlugin({ 26 | patterns: [ 27 | { 28 | from: paths.public, 29 | to: 'assets', 30 | globOptions: { 31 | ignore: ['*.DS_Store'], 32 | }, 33 | noErrorOnMissing: true, 34 | }, 35 | ], 36 | }), 37 | 38 | // Generates an HTML file from a template 39 | // Generates deprecation warning: https://github.com/jantimon/html-webpack-plugin/issues/1501 40 | new HtmlWebpackPlugin({ 41 | title: 'webpack-demo', 42 | favicon: paths.src + '/images/favicon.png', 43 | template: paths.src + '/template.html', // template file 44 | filename: 'index.html', // output file 45 | }), 46 | 47 | // ESLint configuration 48 | new ESLintPlugin({ 49 | files: ['.', 'src', 'config'], 50 | formatter: 'table', 51 | }), 52 | 53 | // Prettier configuration 54 | new PrettierPlugin(), 55 | ], 56 | 57 | // Determine how modules within the project are treated 58 | module: { 59 | rules: [ 60 | // JavaScript: Use Babel to transpile JavaScript files 61 | { test: /\.js$/, use: ['babel-loader'] }, 62 | 63 | // Images: Copy image files to build folder 64 | { test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' }, 65 | 66 | // Fonts and SVGs: Inline files 67 | { test: /\.(woff(2)?|eot|ttf|otf|svg|)$/, type: 'asset/inline' }, 68 | ], 69 | }, 70 | } 71 | -------------------------------------------------------------------------------- /codes/didact/test/00.render-dom-elements.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import './_browser-mock'; 3 | import { render } from '../lib'; 4 | 5 | test.beforeEach(t => { 6 | let root = document.getElementById('root'); 7 | if (!root) { 8 | root = document.createElement('div'); 9 | root.id = 'root'; 10 | document.body.appendChild(root); 11 | } 12 | t.context.root = root; 13 | }); 14 | 15 | test('render div', t => { 16 | const root = t.context.root; 17 | const element = { 18 | type: 'div', 19 | props: {} 20 | }; 21 | render(element, root); 22 | t.is(root.innerHTML, '
'); 23 | }); 24 | 25 | test('render div with children', t => { 26 | const root = t.context.root; 27 | const element = { 28 | type: 'div', 29 | props: { 30 | children: [ 31 | { type: 'b', props: {} }, 32 | { type: 'a', props: { href: 'foo' } } 33 | ] 34 | } 35 | }; 36 | render(element, root); 37 | t.is(root.innerHTML, '
'); 38 | }); 39 | 40 | test('render div with props', t => { 41 | const root = t.context.root; 42 | const element = { 43 | type: 'div', 44 | props: { id: 'foo' } 45 | }; 46 | render(element, root); 47 | t.is(root.innerHTML, '
'); 48 | }); 49 | 50 | test('render span with text child', t => { 51 | const root = t.context.root; 52 | const element = { 53 | type: 'span', 54 | props: { 55 | children: [ 56 | { 57 | type: 'TEXT ELEMENT', 58 | props: { nodeValue: 'Foo' } 59 | } 60 | ] 61 | } 62 | }; 63 | render(element, root); 64 | t.is(root.innerHTML, 'Foo'); 65 | }); 66 | 67 | test('render supports element array', t => { 68 | const root = t.context.root; 69 | const elements = [{ 70 | type: 'span', 71 | props: { 72 | children: [ 73 | { 74 | type: 'TEXT ELEMENT', 75 | props: { nodeValue: 'Foo' } 76 | } 77 | ] 78 | } 79 | }, { 80 | type: 'div', 81 | props: { id: 'foo' } 82 | }]; 83 | render(elements, root); 84 | t.is(root.innerHTML, 'Foo
'); 85 | }); 86 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/inject.js: -------------------------------------------------------------------------------- 1 | import { 2 | hasOwn 3 | } from 'shared/util' 4 | import { 5 | warn, 6 | hasSymbol 7 | } from '../util/index' 8 | import { 9 | defineReactive, 10 | toggleObserving 11 | } from '../observer/index' 12 | 13 | export function initProvide(vm) { 14 | const provide = vm.$options.provide 15 | if (provide) { 16 | vm._provided = typeof provide === 'function' ? 17 | provide.call(vm) : 18 | provide 19 | } 20 | } 21 | 22 | export function initInjections(vm) { 23 | const result = resolveInject(vm.$options.inject, vm) 24 | if (result) { 25 | toggleObserving(false) 26 | Object.keys(result).forEach(key => { 27 | /* istanbul ignore else */ 28 | if (process.env.NODE_ENV !== 'production') { 29 | defineReactive(vm, key, result[key], () => { 30 | warn( 31 | `Avoid mutating an injected value directly since the changes will be ` + 32 | `overwritten whenever the provided component re-renders. ` + 33 | `injection being mutated: "${key}"`, 34 | vm 35 | ) 36 | }) 37 | } else { 38 | defineReactive(vm, key, result[key]) 39 | } 40 | }) 41 | toggleObserving(true) 42 | } 43 | } 44 | 45 | export function resolveInject(inject, vm) { 46 | if (inject) { 47 | // inject is :any because flow is not smart enough to figure out cached 48 | const result = Object.create(null) 49 | const keys = hasSymbol ? 50 | Reflect.ownKeys(inject).filter(key => { 51 | /* istanbul ignore next */ 52 | return Object.getOwnPropertyDescriptor(inject, key).enumerable 53 | }) : 54 | Object.keys(inject) 55 | 56 | for (let i = 0; i < keys.length; i++) { 57 | const key = keys[i] 58 | const provideKey = inject[key].from 59 | let source = vm 60 | while (source) { 61 | if (source._provided && hasOwn(source._provided, provideKey)) { 62 | result[key] = source._provided[provideKey] 63 | break 64 | } 65 | source = source.$parent 66 | } 67 | if (!source) { 68 | if ('default' in inject[key]) { 69 | const provideDefault = inject[key].default 70 | result[key] = typeof provideDefault === 'function' ? 71 | provideDefault.call(vm) : 72 | provideDefault 73 | } else if (process.env.NODE_ENV !== 'production') { 74 | warn(`Injection "${key}" not found`, vm) 75 | } 76 | } 77 | } 78 | return result 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /codes/vue/src/core/instance/proxy.js: -------------------------------------------------------------------------------- 1 | /* not type checking this file because flow doesn't play well with Proxy */ 2 | 3 | import config from 'core/config' 4 | import { warn, makeMap, isNative } from '../util/index' 5 | 6 | let initProxy 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | const allowedGlobals = makeMap( 10 | 'Infinity,undefined,NaN,isFinite,isNaN,' + 11 | 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 12 | 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + 13 | 'require' // for Webpack/Browserify 14 | ) 15 | 16 | const warnNonPresent = (target, key) => { 17 | warn( 18 | `Property or method "${key}" is not defined on the instance but ` + 19 | 'referenced during render. Make sure that this property is reactive, ' + 20 | 'either in the data option, or for class-based components, by ' + 21 | 'initializing the property. ' + 22 | 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', 23 | target 24 | ) 25 | } 26 | 27 | const hasProxy = 28 | typeof Proxy !== 'undefined' && isNative(Proxy) 29 | 30 | if (hasProxy) { 31 | const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact') 32 | config.keyCodes = new Proxy(config.keyCodes, { 33 | set (target, key, value) { 34 | if (isBuiltInModifier(key)) { 35 | warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`) 36 | return false 37 | } else { 38 | target[key] = value 39 | return true 40 | } 41 | } 42 | }) 43 | } 44 | 45 | const hasHandler = { 46 | has (target, key) { 47 | const has = key in target 48 | const isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_') 49 | if (!has && !isAllowed) { 50 | warnNonPresent(target, key) 51 | } 52 | return has || !isAllowed 53 | } 54 | } 55 | 56 | const getHandler = { 57 | get (target, key) { 58 | if (typeof key === 'string' && !(key in target)) { 59 | warnNonPresent(target, key) 60 | } 61 | return target[key] 62 | } 63 | } 64 | 65 | initProxy = function initProxy (vm) { 66 | if (hasProxy) { 67 | // determine which proxy handler to use 68 | const options = vm.$options 69 | const handlers = options.render && options.render._withStripped 70 | ? getHandler 71 | : hasHandler 72 | vm._renderProxy = new Proxy(vm, handlers) 73 | } else { 74 | vm._renderProxy = vm 75 | } 76 | } 77 | } 78 | 79 | export { initProxy } 80 | -------------------------------------------------------------------------------- /codes/didact/src/dom-utils.ts: -------------------------------------------------------------------------------- 1 | import { TEXT_ELEMENT } from './element'; 2 | import { IFiber, IProps, IState } from './interface'; 3 | 4 | const isEvent = (name: string) => name.startsWith('on'); 5 | const isAttribute = (name: string) => 6 | !isEvent(name) && name !== 'children' && name !== 'style'; 7 | const isNew = (prev: IState, next: IState) => (key: string) => prev[key] !== next[key]; 8 | const isGone = (next: IState) => (key: string) => !(key in next); 9 | 10 | export function updateDomProperties(dom: HTMLElement, prevProps: IProps, nextProps: IProps) { 11 | // 解绑之前注册的事件 12 | Object.keys(prevProps) 13 | .filter(isEvent) 14 | .filter( 15 | (key) => !(key in nextProps) || isNew(prevProps, nextProps)(key), 16 | ) 17 | .forEach((name) => { 18 | const eventType = name.toLowerCase().substring(2); 19 | dom.removeEventListener(eventType, prevProps[name]); 20 | }); 21 | 22 | // 删除已去除的 attributes 23 | Object.keys(prevProps) 24 | .filter(isAttribute) 25 | .filter(isGone(nextProps)) 26 | .forEach((name) => { 27 | (dom as IState)[name] = null; 28 | }); 29 | 30 | // 设置添加或更新的 attributes 31 | Object.keys(nextProps) 32 | .filter(isAttribute) 33 | .filter(isNew(prevProps, nextProps)) 34 | .forEach((name) => { 35 | (dom as IState)[name] = nextProps[name]; 36 | }); 37 | 38 | // 重新绑定事件 39 | Object.keys(nextProps) 40 | .filter(isEvent) 41 | .filter(isNew(prevProps, nextProps)) 42 | .forEach((name) => { 43 | const eventType = name.toLowerCase().substring(2); 44 | dom.addEventListener(eventType, nextProps[name]); 45 | }); 46 | 47 | prevProps.style = prevProps.style || {}; 48 | nextProps.style = nextProps.style || {}; 49 | // 更新 style 50 | Object.keys(nextProps.style) 51 | .filter(isNew(prevProps.style, nextProps.style)) 52 | .forEach((key) => { 53 | dom.style[key as any] = (nextProps as any).style[key]; 54 | }); 55 | // 删除已去除的 style 56 | Object.keys(prevProps.style) 57 | .filter(isGone(nextProps.style)) 58 | .forEach((key) => { 59 | dom.style[key as any] = ''; 60 | }); 61 | } 62 | 63 | /** 64 | * 创建对应的 DOM 元素,并根据 props 设置相应属性 65 | * @param fiber 目标 fiber 66 | */ 67 | export function createDomElement(fiber: IFiber) { 68 | const isTextElement = fiber.type === TEXT_ELEMENT; 69 | const dom = isTextElement 70 | ? document.createTextNode('') 71 | : document.createElement(fiber.type as string); 72 | updateDomProperties(dom as HTMLElement, [], fiber.props); 73 | return dom; 74 | } 75 | -------------------------------------------------------------------------------- /codes/minipack/src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const parser = require('@babel/parser'); 4 | const traverse = require('@babel/traverse').default; 5 | const babel = require('@babel/core'); 6 | 7 | let ID = 0; 8 | 9 | function createAsset(filename) { 10 | if (path.extname(filename) === '') { 11 | filename += '.js'; 12 | } 13 | const content = fs.readFileSync(filename, { encoding: 'utf8' }); 14 | const deps = []; 15 | const id = ID++; 16 | const ast = parser.parse(content, { 17 | sourceType: 'module' 18 | }); 19 | 20 | traverse(ast, { 21 | enter(path) { 22 | if (path.node.type === 'ImportDeclaration') { 23 | deps.push(path.node.source.value); 24 | } 25 | } 26 | }); 27 | 28 | const code = babel.transformFromAstSync(ast, content, { 29 | presets: [['@babel/preset-env']] 30 | }).code; 31 | 32 | return { 33 | id, 34 | filename, 35 | ast, 36 | code, 37 | deps 38 | } 39 | } 40 | 41 | function createGraph(entry) { 42 | const mainAsset = createAsset( 43 | path.isAbsolute(entry) ? entry : path.resolve(entry) 44 | ); 45 | const queue = [mainAsset]; 46 | 47 | for(const asset of queue) { 48 | const dirname = path.dirname(asset.filename); 49 | 50 | asset.mapping = {}; 51 | 52 | asset.deps.forEach(relativePath => { 53 | const absolutePath = path.join(dirname, relativePath); 54 | 55 | const child = createAsset(absolutePath); 56 | 57 | asset.mapping[relativePath] = child.id; 58 | 59 | queue.push(child); 60 | }); 61 | } 62 | return queue; 63 | } 64 | 65 | function bundle(graph) { 66 | let modules = ''; 67 | 68 | graph.forEach(mod => { 69 | modules += `${mod.id}: [ 70 | function (require, module, exports) { 71 | ${mod.code} 72 | }, 73 | ${JSON.stringify(mod.mapping)} 74 | ],`; 75 | }); 76 | 77 | const result = ` 78 | (function(modules) { 79 | function require(id) { 80 | const [fn, mapping] = modules[id]; 81 | 82 | function localRequire(relativePath) { 83 | return require(mapping[relativePath]) 84 | } 85 | 86 | const module = { exports: {} }; 87 | 88 | fn(localRequire, module, module.exports); 89 | 90 | return module.exports; 91 | } 92 | 93 | require(0); 94 | })({${modules}}) 95 | `; 96 | 97 | return result; 98 | } 99 | 100 | const graph = createGraph('./demo/entry.js'); 101 | 102 | const result = bundle(graph); 103 | 104 | eval(result) 105 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/vnode.js: -------------------------------------------------------------------------------- 1 | export default class VNode { 2 | // rendered in this component's scope 3 | 4 | 5 | // component instance 6 | // component placeholder node 7 | 8 | // strictly internal 9 | // contains raw HTML? (server only) 10 | // hoisted static node 11 | // necessary for enter transition check 12 | // empty comment placeholder? 13 | // is a cloned node? 14 | // is a v-once node? 15 | // async component factory function 16 | 17 | 18 | 19 | // real context vm for functional nodes 20 | // for SSR caching 21 | // functional scope id support 22 | 23 | constructor( 24 | tag, 25 | data, 26 | children, 27 | text, 28 | elm, 29 | context, 30 | componentOptions, 31 | asyncFactory 32 | ) { 33 | this.tag = tag 34 | this.data = data 35 | this.children = children 36 | this.text = text 37 | this.elm = elm 38 | this.ns = undefined 39 | this.context = context 40 | this.fnContext = undefined 41 | this.fnOptions = undefined 42 | this.fnScopeId = undefined 43 | this.key = data && data.key 44 | this.componentOptions = componentOptions 45 | this.componentInstance = undefined 46 | this.parent = undefined 47 | this.raw = false 48 | this.isStatic = false 49 | this.isRootInsert = true 50 | this.isComment = false 51 | this.isCloned = false 52 | this.isOnce = false 53 | this.asyncFactory = asyncFactory 54 | this.asyncMeta = undefined 55 | this.isAsyncPlaceholder = false 56 | } 57 | 58 | // DEPRECATED: alias for componentInstance for backwards compat. 59 | /* istanbul ignore next */ 60 | get child() { 61 | return this.componentInstance 62 | } 63 | } 64 | 65 | export const createEmptyVNode = (text = '') => { 66 | const node = new VNode() 67 | node.text = text 68 | node.isComment = true 69 | return node 70 | } 71 | 72 | export function createTextVNode(val) { 73 | return new VNode(undefined, undefined, undefined, String(val)) 74 | } 75 | 76 | // optimized shallow clone 77 | // used for static nodes and slot nodes because they may be reused across 78 | // multiple renders, cloning them avoids errors when DOM manipulations rely 79 | // on their elm reference. 80 | export function cloneVNode(vnode) { 81 | const cloned = new VNode( 82 | vnode.tag, 83 | vnode.data, 84 | vnode.children, 85 | vnode.text, 86 | vnode.elm, 87 | vnode.context, 88 | vnode.componentOptions, 89 | vnode.asyncFactory 90 | ) 91 | cloned.ns = vnode.ns 92 | cloned.isStatic = vnode.isStatic 93 | cloned.key = vnode.key 94 | cloned.isComment = vnode.isComment 95 | cloned.fnContext = vnode.fnContext 96 | cloned.fnOptions = vnode.fnOptions 97 | cloned.fnScopeId = vnode.fnScopeId 98 | cloned.asyncMeta = vnode.asyncMeta 99 | cloned.isCloned = true 100 | return cloned 101 | } 102 | -------------------------------------------------------------------------------- /codes/vue/src/core/util/env.js: -------------------------------------------------------------------------------- 1 | // can we use __proto__? 2 | export const hasProto = '__proto__' in {} 3 | 4 | // Browser environment sniffing 5 | export const inBrowser = typeof window !== 'undefined' 6 | export const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform 7 | export const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase() 8 | export const UA = inBrowser && window.navigator.userAgent.toLowerCase() 9 | export const isIE = UA && /msie|trident/.test(UA) 10 | export const isIE9 = UA && UA.indexOf('msie 9.0') > 0 11 | export const isEdge = UA && UA.indexOf('edge/') > 0 12 | export const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android') 13 | export const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios') 14 | export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge 15 | 16 | // Firefox has a "watch" function on Object.prototype... 17 | export const nativeWatch = ({}).watch 18 | 19 | export let supportsPassive = false 20 | if (inBrowser) { 21 | try { 22 | const opts = {} 23 | Object.defineProperty(opts, 'passive', ({ 24 | get() { 25 | /* istanbul ignore next */ 26 | supportsPassive = true 27 | } 28 | })) // https://github.com/facebook/flow/issues/285 29 | window.addEventListener('test-passive', null, opts) 30 | } catch (e) {} 31 | } 32 | 33 | // this needs to be lazy-evaled because vue may be required before 34 | // vue-server-renderer can set VUE_ENV 35 | let _isServer 36 | export const isServerRendering = () => { 37 | if (_isServer === undefined) { 38 | /* istanbul ignore if */ 39 | if (!inBrowser && !inWeex && typeof global !== 'undefined') { 40 | // detect presence of vue-server-renderer and avoid 41 | // Webpack shimming the process 42 | _isServer = global['process'].env.VUE_ENV === 'server' 43 | } else { 44 | _isServer = false 45 | } 46 | } 47 | return _isServer 48 | } 49 | 50 | // detect devtools 51 | export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__ 52 | 53 | /* istanbul ignore next */ 54 | export function isNative(Ctor) { 55 | return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) 56 | } 57 | 58 | export const hasSymbol = 59 | typeof Symbol !== 'undefined' && isNative(Symbol) && 60 | typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys) 61 | 62 | let _Set 63 | /* istanbul ignore if */ // $flow-disable-line 64 | if (typeof Set !== 'undefined' && isNative(Set)) { 65 | // use native Set when available. 66 | _Set = Set 67 | } else { 68 | // a non-standard Set polyfill that only works with primitive keys. 69 | _Set = class Set { 70 | 71 | constructor() { 72 | this.set = Object.create(null) 73 | } 74 | has(key) { 75 | return this.set[key] === true 76 | } 77 | add(key) { 78 | this.set[key] = true 79 | } 80 | clear() { 81 | this.set = Object.create(null) 82 | } 83 | } 84 | } 85 | 86 | export { 87 | _Set 88 | } 89 | -------------------------------------------------------------------------------- /codes/vue/src/core/global-api/extend.js: -------------------------------------------------------------------------------- 1 | import { 2 | ASSET_TYPES 3 | } from '../../shared/constants' 4 | import { 5 | defineComputed, 6 | proxy 7 | } from '../instance/state' 8 | import { 9 | extend, 10 | mergeOptions, 11 | validateComponentName 12 | } from '../util/index' 13 | 14 | export function initExtend(Vue) { 15 | /** 16 | * Each instance constructor, including Vue, has a unique 17 | * cid. This enables us to create wrapped "child 18 | * constructors" for prototypal inheritance and cache them. 19 | */ 20 | Vue.cid = 0 21 | let cid = 1 22 | 23 | /** 24 | * Class inheritance 25 | */ 26 | Vue.extend = function (extendOptions) { 27 | extendOptions = extendOptions || {} 28 | const Super = this 29 | const SuperId = Super.cid 30 | const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) 31 | if (cachedCtors[SuperId]) { 32 | return cachedCtors[SuperId] 33 | } 34 | 35 | const name = extendOptions.name || Super.options.name 36 | if (process.env.NODE_ENV !== 'production' && name) { 37 | validateComponentName(name) 38 | } 39 | 40 | const Sub = function VueComponent(options) { 41 | this._init(options) 42 | } 43 | Sub.prototype = Object.create(Super.prototype) 44 | Sub.prototype.constructor = Sub 45 | Sub.cid = cid++ 46 | Sub.options = mergeOptions( 47 | Super.options, 48 | extendOptions 49 | ) 50 | Sub['super'] = Super 51 | 52 | // For props and computed properties, we define the proxy getters on 53 | // the Vue instances at extension time, on the extended prototype. This 54 | // avoids Object.defineProperty calls for each instance created. 55 | if (Sub.options.props) { 56 | initProps(Sub) 57 | } 58 | if (Sub.options.computed) { 59 | initComputed(Sub) 60 | } 61 | 62 | // allow further extension/mixin/plugin usage 63 | Sub.extend = Super.extend 64 | Sub.mixin = Super.mixin 65 | Sub.use = Super.use 66 | 67 | // create asset registers, so extended classes 68 | // can have their private assets too. 69 | ASSET_TYPES.forEach(function (type) { 70 | Sub[type] = Super[type] 71 | }) 72 | // enable recursive self-lookup 73 | if (name) { 74 | Sub.options.components[name] = Sub 75 | } 76 | 77 | // keep a reference to the super options at extension time. 78 | // later at instantiation we can check if Super's options have 79 | // been updated. 80 | Sub.superOptions = Super.options 81 | Sub.extendOptions = extendOptions 82 | Sub.sealedOptions = extend({}, Sub.options) 83 | 84 | // cache constructor 85 | cachedCtors[SuperId] = Sub 86 | return Sub 87 | } 88 | } 89 | 90 | function initProps(Comp) { 91 | const props = Comp.options.props 92 | for (const key in props) { 93 | proxy(Comp.prototype, `_props`, key) 94 | } 95 | } 96 | 97 | function initComputed(Comp) { 98 | const computed = Comp.options.computed 99 | for (const key in computed) { 100 | defineComputed(Comp.prototype, key, computed[key]) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /codes/vue/src/core/util/debug.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { 3 | noop 4 | } from '../../shared/util' 5 | 6 | export let warn = noop 7 | export let tip = noop 8 | export let generateComponentTrace = (noop) // work around flow check 9 | export let formatComponentName = (noop) 10 | 11 | if (process.env.NODE_ENV !== 'production') { 12 | const hasConsole = typeof console !== 'undefined' 13 | const classifyRE = /(?:^|[-_])(\w)/g 14 | const classify = str => str 15 | .replace(classifyRE, c => c.toUpperCase()) 16 | .replace(/[-_]/g, '') 17 | 18 | warn = (msg, vm) => { 19 | const trace = vm ? generateComponentTrace(vm) : '' 20 | 21 | if (config.warnHandler) { 22 | config.warnHandler.call(null, msg, vm, trace) 23 | } else if (hasConsole && (!config.silent)) { 24 | console.error(`[Vue warn]: ${msg}${trace}`) 25 | } 26 | } 27 | 28 | tip = (msg, vm) => { 29 | if (hasConsole && (!config.silent)) { 30 | console.warn(`[Vue tip]: ${msg}` + ( 31 | vm ? generateComponentTrace(vm) : '' 32 | )) 33 | } 34 | } 35 | 36 | formatComponentName = (vm, includeFile) => { 37 | if (vm.$root === vm) { 38 | return '' 39 | } 40 | const options = typeof vm === 'function' && vm.cid != null ? 41 | vm.options : 42 | vm._isVue ? 43 | vm.$options || vm.constructor.options : 44 | vm || {} 45 | let name = options.name || options._componentTag 46 | const file = options.__file 47 | if (!name && file) { 48 | const match = file.match(/([^/\\]+)\.vue$/) 49 | name = match && match[1] 50 | } 51 | 52 | return ( 53 | (name ? `<${classify(name)}>` : ``) + 54 | (file && includeFile !== false ? ` at ${file}` : '') 55 | ) 56 | } 57 | 58 | const repeat = (str, n) => { 59 | let res = '' 60 | while (n) { 61 | if (n % 2 === 1) res += str 62 | if (n > 1) str += str 63 | n >>= 1 64 | } 65 | return res 66 | } 67 | 68 | generateComponentTrace = vm => { 69 | if (vm._isVue && vm.$parent) { 70 | const tree = [] 71 | let currentRecursiveSequence = 0 72 | while (vm) { 73 | if (tree.length > 0) { 74 | const last = tree[tree.length - 1] 75 | if (last.constructor === vm.constructor) { 76 | currentRecursiveSequence++ 77 | vm = vm.$parent 78 | continue 79 | } else if (currentRecursiveSequence > 0) { 80 | tree[tree.length - 1] = [last, currentRecursiveSequence] 81 | currentRecursiveSequence = 0 82 | } 83 | } 84 | tree.push(vm) 85 | vm = vm.$parent 86 | } 87 | return '\n\nfound in\n\n' + tree 88 | .map((vm, i) => `${ 89 | i === 0 ? '---> ' : repeat(' ', 5 + i * 2) 90 | }${ 91 | Array.isArray(vm) 92 | ? `${formatComponentName(vm[0])}... (${vm[1]} recursive calls)` 93 | : formatComponentName(vm) 94 | }`) 95 | .join('\n') 96 | } else { 97 | return `\n\n(found in ${formatComponentName(vm)})` 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /codes/vue/src/core/vdom/helpers/normalize-children.js: -------------------------------------------------------------------------------- 1 | import VNode, { createTextVNode } from '../../vdom/vnode' 2 | import { 3 | isFalse, 4 | isTrue, 5 | isDef, 6 | isUndef, 7 | isPrimitive 8 | } from '../../../shared/util' 9 | 10 | // The template compiler attempts to minimize the need for normalization by 11 | // statically analyzing the template at compile time. 12 | // 13 | // For plain HTML markup, normalization can be completely skipped because the 14 | // generated render function is guaranteed to return Array. There are 15 | // two cases where extra normalization is needed: 16 | 17 | // 1. When the children contains components - because a functional component 18 | // may return an Array instead of a single root. In this case, just a simple 19 | // normalization is needed - if any child is an Array, we flatten the whole 20 | // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep 21 | // because functional components already normalize their own children. 22 | export function simpleNormalizeChildren (children ) { 23 | for (let i = 0; i < children.length; i++) { 24 | if (Array.isArray(children[i])) { 25 | return Array.prototype.concat.apply([], children) 26 | } 27 | } 28 | return children 29 | } 30 | 31 | // 2. When the children contains constructs that always generated nested Arrays, 32 | // e.g.