modified .gitignore
This commit is contained in:
+75
-20
@@ -6,23 +6,31 @@
|
||||
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/dm/vue.svg" alt="Downloads"></a>
|
||||
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/v/vue.svg" alt="Version"></a>
|
||||
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/l/vue.svg" alt="License"></a>
|
||||
<a href="https://chat.vuejs.org/"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat">
|
||||
<br>
|
||||
<a href="https://saucelabs.com/u/vuejs"><img src="https://saucelabs.com/browser-matrix/vuejs.svg" alt="Sauce Test Status"></a>
|
||||
</p>
|
||||
|
||||
## Supporting Vue.js
|
||||
|
||||
Vue.js is an MIT-licensed open source project. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, check out [Vue.js' Patreon campaign](https://www.patreon.com/evanyou).
|
||||
Vue.js is an MIT-licensed open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/BACKERS.md). If you'd like to join them, please consider:
|
||||
|
||||
- [Become a backer or sponsor on Patreon](https://www.patreon.com/evanyou).
|
||||
- [Become a backer or sponsor on OpenCollective](https://opencollective.com/vuejs).
|
||||
|
||||
#### What's the difference between Patreon and OpenCollective?
|
||||
|
||||
Funds donated via Patreon goes directly to support Evan You's full-time work on Vue.js. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses by core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
|
||||
|
||||
### Sponsors via Patreon
|
||||
|
||||
<p align="center">
|
||||
<b>Special thanks to the generous sponsorship by:</b>
|
||||
<br><br>
|
||||
<a href="https://stdlib.com">
|
||||
<img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/stdlib.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://www.upyun.com/?utm_source=vue&utm_medium=ad&utm_content=github">
|
||||
<img width="160px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/upyun-small.png">
|
||||
<a href="https://xiaozhuanlan.com">
|
||||
<img width="160px" src="https://raw.githubusercontent.com/vuejs/cn.vuejs.org/master/themes/vue/source/images/xiaozhuanlan.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://deepstreamhub.com" target="_blank">
|
||||
@@ -53,10 +61,6 @@ Vue.js is an MIT-licensed open source project. Its ongoing development is made p
|
||||
<img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/monterail.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://www.trisoft.ro/" target="_blank">
|
||||
<img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/trisoft.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://www.2mhost.com/" target="_blank">
|
||||
<img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/2mhost.png">
|
||||
</a>
|
||||
@@ -69,19 +73,62 @@ Vue.js is an MIT-licensed open source project. Its ongoing development is made p
|
||||
<img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/tmvuejs2.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://famebroker.com" target="_blank">
|
||||
<img width="130px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/famebroker.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://fancygrid.com" target="_blank">
|
||||
<img width="120px" src="http://fancygrid.com/logo/logo.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://component.io/" target="_blank">
|
||||
<img width="130px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/component_io.png">
|
||||
</a>
|
||||
<br><br>
|
||||
<a href="https://www.v2ex.com/t/379389" target="_blank">
|
||||
<img width="130px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/v2exer.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### Sponsors via OpenCollective
|
||||
|
||||
#### Platinum
|
||||
|
||||
<a href="https://opencollective.com/vuejs/platinumsponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/platinumsponsor/0/avatar.svg"></a>
|
||||
|
||||
#### Gold
|
||||
|
||||
<a href="https://opencollective.com/vuejs/goldsponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/goldsponsor/0/avatar.svg"></a>
|
||||
|
||||
#### Silver
|
||||
|
||||
<a href="https://opencollective.com/vuejs/silversponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/silversponsor/0/avatar.svg"></a>
|
||||
|
||||
#### Bronze
|
||||
|
||||
<a href="https://opencollective.com/vuejs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/1/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/2/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/3/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/4/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/5/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/6/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/7/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/8/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/9/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/10/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/11/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/12/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/13/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/14/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/15/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/16/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/17/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/18/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/19/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/20/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/21/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/22/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/23/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/24/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/25/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/26/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/27/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/28/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/vuejs/sponsor/29/website" target="_blank"><img src="https://opencollective.com/vuejs/sponsor/29/avatar.svg"></a>
|
||||
|
||||
## Intro
|
||||
|
||||
Vue.js is a library for building interactive web interfaces. It provides data-reactive components with a simple and flexible API. Core features include:
|
||||
@@ -92,11 +139,15 @@ Vue.js is a library for building interactive web interfaces. It provides data-re
|
||||
- [Flexible transition effect system](https://vuejs.org/guide/transitions.html)
|
||||
- Fast without the need for complex optimization
|
||||
|
||||
Note that Vue.js only supports [ES5-compliant browsers](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). To check out live examples and docs, visit [vuejs.org](https://vuejs.org).
|
||||
Note that Vue.js only supports [ES5-compliant browsers](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported).
|
||||
|
||||
## Documentation
|
||||
|
||||
To check out live examples and docs, visit [vuejs.org](https://vuejs.org).
|
||||
|
||||
## Questions
|
||||
|
||||
For questions and support please use the [Gitter chat room](https://gitter.im/vuejs/vue) or [the official forum](http://forum.vuejs.org). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
For questions and support please use the [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
|
||||
## Issues
|
||||
|
||||
@@ -106,9 +157,13 @@ Please make sure to read the [Issue Reporting Checklist](https://github.com/vuej
|
||||
|
||||
Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md) before making a pull request. If you have a Vue-related project/component/tool, add it with a pull-request to [this curated list](https://github.com/vuejs/awesome-vue)!
|
||||
|
||||
Thank you to all the people who already contributed to Vue!
|
||||
|
||||
<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
|
||||
|
||||
## Changelog
|
||||
|
||||
Details changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
|
||||
Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
|
||||
|
||||
## Stay In Touch
|
||||
|
||||
|
||||
+1171
-670
File diff suppressed because it is too large
Load Diff
+1171
-670
File diff suppressed because it is too large
Load Diff
+1168
-670
File diff suppressed because it is too large
Load Diff
+2
-4
File diff suppressed because one or more lines are too long
+700
-291
File diff suppressed because it is too large
Load Diff
+700
-291
File diff suppressed because it is too large
Load Diff
+697
-291
File diff suppressed because it is too large
Load Diff
+2
-3
File diff suppressed because one or more lines are too long
+83
-107
@@ -1,151 +1,125 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
{
|
||||
"raw": "vue@^2.1.10",
|
||||
"scope": null,
|
||||
"escapedName": "vue",
|
||||
"name": "vue",
|
||||
"rawSpec": "^2.1.10",
|
||||
"spec": ">=2.1.10 <3.0.0",
|
||||
"type": "range"
|
||||
},
|
||||
"c:\\xampp\\htdocs\\laravel"
|
||||
]
|
||||
],
|
||||
"_from": "vue@>=2.1.10 <3.0.0",
|
||||
"_id": "vue@2.3.4",
|
||||
"_inCache": true,
|
||||
"_from": "vue@2.4.3",
|
||||
"_id": "vue@2.4.3",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-k6zkIBR0KsE0DLUDGdRLooX/4iRUbc3T2FyrJs4YhVySbjGwS3k5c2HRCHyXo6lg1aeAF9rg3uiJDRz0J7nbDA==",
|
||||
"_location": "/vue",
|
||||
"_nodeVersion": "8.0.0",
|
||||
"_npmOperationalInternal": {
|
||||
"host": "s3://npm-registry-packages",
|
||||
"tmp": "tmp/vue-2.3.4.tgz_1496897690369_0.5197095186449587"
|
||||
},
|
||||
"_npmUser": {
|
||||
"name": "yyx990803",
|
||||
"email": "yyx990803@gmail.com"
|
||||
},
|
||||
"_npmVersion": "5.0.0",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"raw": "vue@^2.1.10",
|
||||
"scope": null,
|
||||
"escapedName": "vue",
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "vue@2.4.3",
|
||||
"name": "vue",
|
||||
"rawSpec": "^2.1.10",
|
||||
"spec": ">=2.1.10 <3.0.0",
|
||||
"type": "range"
|
||||
"escapedName": "vue",
|
||||
"rawSpec": "2.4.3",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.4.3"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#DEV:/"
|
||||
"#DEV:/",
|
||||
"#USER"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/vue/-/vue-2.3.4.tgz",
|
||||
"_shasum": "5ec3b87a191da8090bbef56b7cfabd4158038171",
|
||||
"_shrinkwrap": null,
|
||||
"_spec": "vue@^2.1.10",
|
||||
"_where": "c:\\xampp\\htdocs\\laravel",
|
||||
"_resolved": "https://registry.npmjs.org/vue/-/vue-2.4.3.tgz",
|
||||
"_shasum": "55fee0ec509cf2e10aa73b34b15219e92a9ab9ea",
|
||||
"_spec": "vue@2.4.3",
|
||||
"_where": "D:\\dev\\dp\\dp",
|
||||
"author": {
|
||||
"name": "Evan You"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/vue/issues"
|
||||
},
|
||||
"dependencies": {},
|
||||
"bundleDependencies": false,
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Reactive, component-oriented view layer for modern web interfaces.",
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.9.0",
|
||||
"babel-eslint": "^7.1.0",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.2",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-plugin-istanbul": "^4.0.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-istanbul": "^4.1.4",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.2.0",
|
||||
"babel-preset-es2015": "^6.9.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.4.3",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-flow-vue": "^1.0.0",
|
||||
"buble": "^0.15.2",
|
||||
"chalk": "^1.1.3",
|
||||
"chromedriver": "^2.21.2",
|
||||
"chromedriver": "^2.30.1",
|
||||
"codecov.io": "^0.1.6",
|
||||
"cross-spawn": "^5.0.1",
|
||||
"commitizen": "^2.9.6",
|
||||
"conventional-changelog": "^1.1.3",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"cz-conventional-changelog": "^2.0.0",
|
||||
"de-indent": "^1.0.2",
|
||||
"es6-promise": "^4.0.5",
|
||||
"eslint": "^3.10.1",
|
||||
"eslint-config-vue": "^2.0.1",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"eslint-plugin-flowtype": "^2.16.0",
|
||||
"eslint-plugin-jasmine": "^2.1.0",
|
||||
"eslint-plugin-vue": "^2.0.0",
|
||||
"file-loader": "^0.10.1",
|
||||
"flow-bin": "^0.45.0",
|
||||
"es6-promise": "^4.1.0",
|
||||
"eslint": "^3.0.0",
|
||||
"eslint-loader": "^1.7.1",
|
||||
"eslint-plugin-flowtype": "^2.34.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
"eslint-plugin-vue-libs": "^1.2.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"flow-bin": "^0.48.0",
|
||||
"hash-sum": "^1.0.2",
|
||||
"he": "^1.1.0",
|
||||
"http-server": "^0.9.0",
|
||||
"jasmine": "^2.5.2",
|
||||
"jasmine-core": "^2.5.2",
|
||||
"karma": "^1.1.0",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.0.0",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-mocha-reporter": "^2.0.4",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"he": "^1.1.1",
|
||||
"http-server": "^0.10.0",
|
||||
"jasmine": "^2.6.0",
|
||||
"jasmine-core": "^2.6.3",
|
||||
"karma": "^1.7.0",
|
||||
"karma-chrome-launcher": "^2.1.1",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-firefox-launcher": "^1.0.1",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-mocha-reporter": "^2.2.3",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"karma-safari-launcher": "^1.0.0",
|
||||
"karma-sauce-launcher": "^1.0.0",
|
||||
"karma-sourcemap-loader": "^0.3.0",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"lodash": "^4.17.1",
|
||||
"karma-sauce-launcher": "^1.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^2.0.3",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash.template": "^4.4.0",
|
||||
"lodash.uniq": "^4.5.0",
|
||||
"lru-cache": "^4.0.2",
|
||||
"nightwatch": "^0.9.9",
|
||||
"lru-cache": "^4.1.1",
|
||||
"nightwatch": "^0.9.16",
|
||||
"nightwatch-helpers": "^1.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.1",
|
||||
"resolve": "^1.2.0",
|
||||
"rollup": "^0.41.4",
|
||||
"rollup-plugin-alias": "^1.2.0",
|
||||
"rollup-plugin-babel": "^2.4.0",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"resolve": "^1.3.3",
|
||||
"rollup": "^0.45.1",
|
||||
"rollup-plugin-alias": "^1.3.1",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-buble": "^0.15.0",
|
||||
"rollup-plugin-commonjs": "^8.0.2",
|
||||
"rollup-plugin-flow-no-whitespace": "^1.0.0",
|
||||
"rollup-plugin-replace": "^1.1.0",
|
||||
"rollup-watch": "^3.2.2",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"rollup-plugin-replace": "^1.1.1",
|
||||
"rollup-watch": "^4.0.0",
|
||||
"selenium-server": "^2.53.1",
|
||||
"serialize-javascript": "^1.3.0",
|
||||
"typescript": "^2.1.6",
|
||||
"uglify-js": "^2.6.2",
|
||||
"webpack": "^2.2.0",
|
||||
"weex-js-runtime": "^0.17.0-alpha4",
|
||||
"weex-vdom-tester": "^0.1.4"
|
||||
},
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"integrity": "sha512-oLCxuVcVQ2inwSbS7B+zfjB6CSjgQ0yyCOzPcg7S5CXeOCbtkaiN5frR6MtwvrveqbG86OsGd9jWf6JsGyQkLw==",
|
||||
"shasum": "5ec3b87a191da8090bbef56b7cfabd4158038171",
|
||||
"tarball": "https://registry.npmjs.org/vue/-/vue-2.3.4.tgz"
|
||||
"shelljs": "^0.7.8",
|
||||
"typescript": "^2.3.4",
|
||||
"uglify-js": "^3.0.15",
|
||||
"webpack": "^2.6.1",
|
||||
"weex-js-runtime": "^0.20.5",
|
||||
"weex-vdom-tester": "^0.2.0"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist/*.js",
|
||||
"types/*.d.ts"
|
||||
],
|
||||
"gitHead": "1f9416d514d80a99eb45184459fdf390405967ec",
|
||||
"homepage": "https://github.com/vuejs/vue#readme",
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "dist/vue.runtime.common.js",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "yyx990803",
|
||||
"email": "yyx990803@gmail.com"
|
||||
}
|
||||
],
|
||||
"module": "dist/vue.runtime.esm.js",
|
||||
"name": "vue",
|
||||
"optionalDependencies": {},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/vue.git"
|
||||
@@ -155,30 +129,32 @@
|
||||
"build": "node build/build.js",
|
||||
"build:ssr": "npm run build -- vue.runtime.common.js,vue-server-renderer",
|
||||
"build:weex": "npm run build -- weex-vue-framework,weex-template-compiler",
|
||||
"commit": "git-cz",
|
||||
"dev": "rollup -w -c build/config.js --environment TARGET:web-full-dev",
|
||||
"dev:cjs": "rollup -w -c build/config.js --environment TARGET:web-runtime-cjs",
|
||||
"dev:compiler": "rollup -w -c build/config.js --environment TARGET:web-compiler ",
|
||||
"dev:esm": "rollup -w -c build/config.js --environment TARGET:web-runtime-esm",
|
||||
"dev:ssr": "rollup -w -c build/config.js --environment TARGET:web-server-renderer",
|
||||
"dev:test": "karma start build/karma.dev.config.js",
|
||||
"dev:test": "karma start test/unit/karma.dev.config.js",
|
||||
"dev:weex": "rollup -w -c build/config.js --environment TARGET:weex-framework ",
|
||||
"dev:weex:compiler": "rollup -w -c build/config.js --environment TARGET:weex-compiler ",
|
||||
"flow": "flow check",
|
||||
"install:hooks": "ln -fs ../../build/git-hooks/pre-commit .git/hooks/pre-commit",
|
||||
"lint": "eslint src build test",
|
||||
"release": "bash build/release.sh",
|
||||
"release:note": "node build/gen-release-note.js",
|
||||
"release:weex": "bash build/release-weex.sh",
|
||||
"sauce": "karma start build/karma.sauce.config.js",
|
||||
"sauce": "karma start test/unit/karma.sauce.config.js",
|
||||
"setup": "node build/install-hooks.js",
|
||||
"test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex",
|
||||
"test:cover": "karma start build/karma.cover.config.js",
|
||||
"test:cover": "karma start test/unit/karma.cover.config.js",
|
||||
"test:e2e": "npm run build -- vue.min.js && node test/e2e/runner.js",
|
||||
"test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
|
||||
"test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.json",
|
||||
"test:types": "tsc -p ./types/test/tsconfig.json",
|
||||
"test:unit": "karma start build/karma.unit.config.js",
|
||||
"test:unit": "karma start test/unit/karma.unit.config.js",
|
||||
"test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.json"
|
||||
},
|
||||
"typings": "types/index.d.ts",
|
||||
"unpkg": "dist/vue.js",
|
||||
"version": "2.3.4"
|
||||
"version": "2.4.3"
|
||||
}
|
||||
|
||||
+166
-103
@@ -1,73 +1,76 @@
|
||||
/* @flow */
|
||||
|
||||
import { genHandlers } from './events'
|
||||
import { baseWarn, pluckModuleFunction } from '../helpers'
|
||||
import baseDirectives from '../directives/index'
|
||||
import { camelize, no } from 'shared/util'
|
||||
import { camelize, no, extend } from 'shared/util'
|
||||
import { baseWarn, pluckModuleFunction } from '../helpers'
|
||||
|
||||
type TransformFunction = (el: ASTElement, code: string) => string;
|
||||
type DataGenFunction = (el: ASTElement) => string;
|
||||
type DirectiveFunction = (el: ASTElement, dir: ASTDirective, warn: Function) => boolean;
|
||||
|
||||
// configurable state
|
||||
let warn
|
||||
let transforms: Array<TransformFunction>
|
||||
let dataGenFns: Array<DataGenFunction>
|
||||
let platformDirectives
|
||||
let isPlatformReservedTag
|
||||
let staticRenderFns
|
||||
let onceCount
|
||||
let currentOptions
|
||||
export class CodegenState {
|
||||
options: CompilerOptions;
|
||||
warn: Function;
|
||||
transforms: Array<TransformFunction>;
|
||||
dataGenFns: Array<DataGenFunction>;
|
||||
directives: { [key: string]: DirectiveFunction };
|
||||
maybeComponent: (el: ASTElement) => boolean;
|
||||
onceId: number;
|
||||
staticRenderFns: Array<string>;
|
||||
|
||||
constructor (options: CompilerOptions) {
|
||||
this.options = options
|
||||
this.warn = options.warn || baseWarn
|
||||
this.transforms = pluckModuleFunction(options.modules, 'transformCode')
|
||||
this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
||||
this.directives = extend(extend({}, baseDirectives), options.directives)
|
||||
const isReservedTag = options.isReservedTag || no
|
||||
this.maybeComponent = (el: ASTElement) => !isReservedTag(el.tag)
|
||||
this.onceId = 0
|
||||
this.staticRenderFns = []
|
||||
}
|
||||
}
|
||||
|
||||
export type CodegenResult = {
|
||||
render: string,
|
||||
staticRenderFns: Array<string>
|
||||
};
|
||||
|
||||
export function generate (
|
||||
ast: ASTElement | void,
|
||||
options: CompilerOptions
|
||||
): {
|
||||
render: string,
|
||||
staticRenderFns: Array<string>
|
||||
} {
|
||||
// save previous staticRenderFns so generate calls can be nested
|
||||
const prevStaticRenderFns: Array<string> = staticRenderFns
|
||||
const currentStaticRenderFns: Array<string> = staticRenderFns = []
|
||||
const prevOnceCount = onceCount
|
||||
onceCount = 0
|
||||
currentOptions = options
|
||||
warn = options.warn || baseWarn
|
||||
transforms = pluckModuleFunction(options.modules, 'transformCode')
|
||||
dataGenFns = pluckModuleFunction(options.modules, 'genData')
|
||||
platformDirectives = options.directives || {}
|
||||
isPlatformReservedTag = options.isReservedTag || no
|
||||
const code = ast ? genElement(ast) : '_c("div")'
|
||||
staticRenderFns = prevStaticRenderFns
|
||||
onceCount = prevOnceCount
|
||||
): CodegenResult {
|
||||
const state = new CodegenState(options)
|
||||
const code = ast ? genElement(ast, state) : '_c("div")'
|
||||
return {
|
||||
render: `with(this){return ${code}}`,
|
||||
staticRenderFns: currentStaticRenderFns
|
||||
staticRenderFns: state.staticRenderFns
|
||||
}
|
||||
}
|
||||
|
||||
function genElement (el: ASTElement): string {
|
||||
export function genElement (el: ASTElement, state: CodegenState): string {
|
||||
if (el.staticRoot && !el.staticProcessed) {
|
||||
return genStatic(el)
|
||||
return genStatic(el, state)
|
||||
} else if (el.once && !el.onceProcessed) {
|
||||
return genOnce(el)
|
||||
return genOnce(el, state)
|
||||
} else if (el.for && !el.forProcessed) {
|
||||
return genFor(el)
|
||||
return genFor(el, state)
|
||||
} else if (el.if && !el.ifProcessed) {
|
||||
return genIf(el)
|
||||
return genIf(el, state)
|
||||
} else if (el.tag === 'template' && !el.slotTarget) {
|
||||
return genChildren(el) || 'void 0'
|
||||
return genChildren(el, state) || 'void 0'
|
||||
} else if (el.tag === 'slot') {
|
||||
return genSlot(el)
|
||||
return genSlot(el, state)
|
||||
} else {
|
||||
// component or element
|
||||
let code
|
||||
if (el.component) {
|
||||
code = genComponent(el.component, el)
|
||||
code = genComponent(el.component, el, state)
|
||||
} else {
|
||||
const data = el.plain ? undefined : genData(el)
|
||||
const data = el.plain ? undefined : genData(el, state)
|
||||
|
||||
const children = el.inlineTemplate ? null : genChildren(el, true)
|
||||
const children = el.inlineTemplate ? null : genChildren(el, state, true)
|
||||
code = `_c('${el.tag}'${
|
||||
data ? `,${data}` : '' // data
|
||||
}${
|
||||
@@ -75,25 +78,25 @@ function genElement (el: ASTElement): string {
|
||||
})`
|
||||
}
|
||||
// module transforms
|
||||
for (let i = 0; i < transforms.length; i++) {
|
||||
code = transforms[i](el, code)
|
||||
for (let i = 0; i < state.transforms.length; i++) {
|
||||
code = state.transforms[i](el, code)
|
||||
}
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
||||
// hoist static sub-trees out
|
||||
function genStatic (el: ASTElement): string {
|
||||
function genStatic (el: ASTElement, state: CodegenState): string {
|
||||
el.staticProcessed = true
|
||||
staticRenderFns.push(`with(this){return ${genElement(el)}}`)
|
||||
return `_m(${staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})`
|
||||
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
|
||||
return `_m(${state.staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})`
|
||||
}
|
||||
|
||||
// v-once
|
||||
function genOnce (el: ASTElement): string {
|
||||
function genOnce (el: ASTElement, state: CodegenState): string {
|
||||
el.onceProcessed = true
|
||||
if (el.if && !el.ifProcessed) {
|
||||
return genIf(el)
|
||||
return genIf(el, state)
|
||||
} else if (el.staticInFor) {
|
||||
let key = ''
|
||||
let parent = el.parent
|
||||
@@ -105,51 +108,76 @@ function genOnce (el: ASTElement): string {
|
||||
parent = parent.parent
|
||||
}
|
||||
if (!key) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
process.env.NODE_ENV !== 'production' && state.warn(
|
||||
`v-once can only be used inside v-for that is keyed. `
|
||||
)
|
||||
return genElement(el)
|
||||
return genElement(el, state)
|
||||
}
|
||||
return `_o(${genElement(el)},${onceCount++}${key ? `,${key}` : ``})`
|
||||
return `_o(${genElement(el, state)},${state.onceId++},${key})`
|
||||
} else {
|
||||
return genStatic(el)
|
||||
return genStatic(el, state)
|
||||
}
|
||||
}
|
||||
|
||||
function genIf (el: any): string {
|
||||
export function genIf (
|
||||
el: any,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altEmpty?: string
|
||||
): string {
|
||||
el.ifProcessed = true // avoid recursion
|
||||
return genIfConditions(el.ifConditions.slice())
|
||||
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
|
||||
}
|
||||
|
||||
function genIfConditions (conditions: ASTIfConditions): string {
|
||||
function genIfConditions (
|
||||
conditions: ASTIfConditions,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altEmpty?: string
|
||||
): string {
|
||||
if (!conditions.length) {
|
||||
return '_e()'
|
||||
return altEmpty || '_e()'
|
||||
}
|
||||
|
||||
const condition = conditions.shift()
|
||||
if (condition.exp) {
|
||||
return `(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions)}`
|
||||
return `(${condition.exp})?${
|
||||
genTernaryExp(condition.block)
|
||||
}:${
|
||||
genIfConditions(conditions, state, altGen, altEmpty)
|
||||
}`
|
||||
} else {
|
||||
return `${genTernaryExp(condition.block)}`
|
||||
}
|
||||
|
||||
// v-if with v-once should generate code like (a)?_m(0):_m(1)
|
||||
function genTernaryExp (el) {
|
||||
return el.once ? genOnce(el) : genElement(el)
|
||||
return altGen
|
||||
? altGen(el, state)
|
||||
: el.once
|
||||
? genOnce(el, state)
|
||||
: genElement(el, state)
|
||||
}
|
||||
}
|
||||
|
||||
function genFor (el: any): string {
|
||||
export function genFor (
|
||||
el: any,
|
||||
state: CodegenState,
|
||||
altGen?: Function,
|
||||
altHelper?: string
|
||||
): string {
|
||||
const exp = el.for
|
||||
const alias = el.alias
|
||||
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
|
||||
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
maybeComponent(el) && el.tag !== 'slot' && el.tag !== 'template' && !el.key
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
state.maybeComponent(el) &&
|
||||
el.tag !== 'slot' &&
|
||||
el.tag !== 'template' &&
|
||||
!el.key
|
||||
) {
|
||||
warn(
|
||||
state.warn(
|
||||
`<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` +
|
||||
`v-for should have explicit keys. ` +
|
||||
`See https://vuejs.org/guide/list.html#key for more info.`,
|
||||
@@ -158,18 +186,18 @@ function genFor (el: any): string {
|
||||
}
|
||||
|
||||
el.forProcessed = true // avoid recursion
|
||||
return `_l((${exp}),` +
|
||||
return `${altHelper || '_l'}((${exp}),` +
|
||||
`function(${alias}${iterator1}${iterator2}){` +
|
||||
`return ${genElement(el)}` +
|
||||
`return ${(altGen || genElement)(el, state)}` +
|
||||
'})'
|
||||
}
|
||||
|
||||
function genData (el: ASTElement): string {
|
||||
export function genData (el: ASTElement, state: CodegenState): string {
|
||||
let data = '{'
|
||||
|
||||
// directives first.
|
||||
// directives may mutate the el's other properties before they are generated.
|
||||
const dirs = genDirectives(el)
|
||||
const dirs = genDirectives(el, state)
|
||||
if (dirs) data += dirs + ','
|
||||
|
||||
// key
|
||||
@@ -192,8 +220,8 @@ function genData (el: ASTElement): string {
|
||||
data += `tag:"${el.tag}",`
|
||||
}
|
||||
// module data generation functions
|
||||
for (let i = 0; i < dataGenFns.length; i++) {
|
||||
data += dataGenFns[i](el)
|
||||
for (let i = 0; i < state.dataGenFns.length; i++) {
|
||||
data += state.dataGenFns[i](el)
|
||||
}
|
||||
// attributes
|
||||
if (el.attrs) {
|
||||
@@ -205,10 +233,10 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// event handlers
|
||||
if (el.events) {
|
||||
data += `${genHandlers(el.events, false, warn)},`
|
||||
data += `${genHandlers(el.events, false, state.warn)},`
|
||||
}
|
||||
if (el.nativeEvents) {
|
||||
data += `${genHandlers(el.nativeEvents, true, warn)},`
|
||||
data += `${genHandlers(el.nativeEvents, true, state.warn)},`
|
||||
}
|
||||
// slot target
|
||||
if (el.slotTarget) {
|
||||
@@ -216,7 +244,7 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// scoped slots
|
||||
if (el.scopedSlots) {
|
||||
data += `${genScopedSlots(el.scopedSlots)},`
|
||||
data += `${genScopedSlots(el.scopedSlots, state)},`
|
||||
}
|
||||
// component v-model
|
||||
if (el.model) {
|
||||
@@ -230,7 +258,7 @@ function genData (el: ASTElement): string {
|
||||
}
|
||||
// inline-template
|
||||
if (el.inlineTemplate) {
|
||||
const inlineTemplate = genInlineTemplate(el)
|
||||
const inlineTemplate = genInlineTemplate(el, state)
|
||||
if (inlineTemplate) {
|
||||
data += `${inlineTemplate},`
|
||||
}
|
||||
@@ -240,10 +268,14 @@ function genData (el: ASTElement): string {
|
||||
if (el.wrapData) {
|
||||
data = el.wrapData(data)
|
||||
}
|
||||
// v-on data wrap
|
||||
if (el.wrapListeners) {
|
||||
data = el.wrapListeners(data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function genDirectives (el: ASTElement): string | void {
|
||||
function genDirectives (el: ASTElement, state: CodegenState): string | void {
|
||||
const dirs = el.directives
|
||||
if (!dirs) return
|
||||
let res = 'directives:['
|
||||
@@ -252,11 +284,11 @@ function genDirectives (el: ASTElement): string | void {
|
||||
for (i = 0, l = dirs.length; i < l; i++) {
|
||||
dir = dirs[i]
|
||||
needRuntime = true
|
||||
const gen: DirectiveFunction = platformDirectives[dir.name] || baseDirectives[dir.name]
|
||||
const gen: DirectiveFunction = state.directives[dir.name]
|
||||
if (gen) {
|
||||
// compile-time directive that manipulates AST.
|
||||
// returns true if it also needs a runtime counterpart.
|
||||
needRuntime = !!gen(el, dir, warn)
|
||||
needRuntime = !!gen(el, dir, state.warn)
|
||||
}
|
||||
if (needRuntime) {
|
||||
hasRuntime = true
|
||||
@@ -274,15 +306,15 @@ function genDirectives (el: ASTElement): string | void {
|
||||
}
|
||||
}
|
||||
|
||||
function genInlineTemplate (el: ASTElement): ?string {
|
||||
function genInlineTemplate (el: ASTElement, state: CodegenState): ?string {
|
||||
const ast = el.children[0]
|
||||
if (process.env.NODE_ENV !== 'production' && (
|
||||
el.children.length > 1 || ast.type !== 1
|
||||
)) {
|
||||
warn('Inline-template components must have exactly one child element.')
|
||||
state.warn('Inline-template components must have exactly one child element.')
|
||||
}
|
||||
if (ast.type === 1) {
|
||||
const inlineRenderFns = generate(ast, currentOptions)
|
||||
const inlineRenderFns = generate(ast, state.options)
|
||||
return `inlineTemplate:{render:function(){${
|
||||
inlineRenderFns.render
|
||||
}},staticRenderFns:[${
|
||||
@@ -291,24 +323,37 @@ function genInlineTemplate (el: ASTElement): ?string {
|
||||
}
|
||||
}
|
||||
|
||||
function genScopedSlots (slots: { [key: string]: ASTElement }): string {
|
||||
function genScopedSlots (
|
||||
slots: { [key: string]: ASTElement },
|
||||
state: CodegenState
|
||||
): string {
|
||||
return `scopedSlots:_u([${
|
||||
Object.keys(slots).map(key => genScopedSlot(key, slots[key])).join(',')
|
||||
Object.keys(slots).map(key => {
|
||||
return genScopedSlot(key, slots[key], state)
|
||||
}).join(',')
|
||||
}])`
|
||||
}
|
||||
|
||||
function genScopedSlot (key: string, el: ASTElement) {
|
||||
function genScopedSlot (
|
||||
key: string,
|
||||
el: ASTElement,
|
||||
state: CodegenState
|
||||
): string {
|
||||
if (el.for && !el.forProcessed) {
|
||||
return genForScopedSlot(key, el)
|
||||
return genForScopedSlot(key, el, state)
|
||||
}
|
||||
return `{key:${key},fn:function(${String(el.attrsMap.scope)}){` +
|
||||
`return ${el.tag === 'template'
|
||||
? genChildren(el) || 'void 0'
|
||||
: genElement(el)
|
||||
? genChildren(el, state) || 'void 0'
|
||||
: genElement(el, state)
|
||||
}}}`
|
||||
}
|
||||
|
||||
function genForScopedSlot (key: string, el: any) {
|
||||
function genForScopedSlot (
|
||||
key: string,
|
||||
el: any,
|
||||
state: CodegenState
|
||||
): string {
|
||||
const exp = el.for
|
||||
const alias = el.alias
|
||||
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
|
||||
@@ -316,11 +361,17 @@ function genForScopedSlot (key: string, el: any) {
|
||||
el.forProcessed = true // avoid recursion
|
||||
return `_l((${exp}),` +
|
||||
`function(${alias}${iterator1}${iterator2}){` +
|
||||
`return ${genScopedSlot(key, el)}` +
|
||||
`return ${genScopedSlot(key, el, state)}` +
|
||||
'})'
|
||||
}
|
||||
|
||||
function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
export function genChildren (
|
||||
el: ASTElement,
|
||||
state: CodegenState,
|
||||
checkSkip?: boolean,
|
||||
altGenElement?: Function,
|
||||
altGenNode?: Function
|
||||
): string | void {
|
||||
const children = el.children
|
||||
if (children.length) {
|
||||
const el: any = children[0]
|
||||
@@ -330,10 +381,13 @@ function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
el.tag !== 'template' &&
|
||||
el.tag !== 'slot'
|
||||
) {
|
||||
return genElement(el)
|
||||
return (altGenElement || genElement)(el, state)
|
||||
}
|
||||
const normalizationType = checkSkip ? getNormalizationType(children) : 0
|
||||
return `[${children.map(genNode).join(',')}]${
|
||||
const normalizationType = checkSkip
|
||||
? getNormalizationType(children, state.maybeComponent)
|
||||
: 0
|
||||
const gen = altGenNode || genNode
|
||||
return `[${children.map(c => gen(c, state)).join(',')}]${
|
||||
normalizationType ? `,${normalizationType}` : ''
|
||||
}`
|
||||
}
|
||||
@@ -343,7 +397,10 @@ function genChildren (el: ASTElement, checkSkip?: boolean): string | void {
|
||||
// 0: no normalization needed
|
||||
// 1: simple normalization needed (possible 1-level deep nested array)
|
||||
// 2: full normalization needed
|
||||
function getNormalizationType (children: Array<ASTNode>): number {
|
||||
function getNormalizationType (
|
||||
children: Array<ASTNode>,
|
||||
maybeComponent: (el: ASTElement) => boolean
|
||||
): number {
|
||||
let res = 0
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const el: ASTNode = children[i]
|
||||
@@ -367,28 +424,30 @@ function needsNormalization (el: ASTElement): boolean {
|
||||
return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
|
||||
}
|
||||
|
||||
function maybeComponent (el: ASTElement): boolean {
|
||||
return !isPlatformReservedTag(el.tag)
|
||||
}
|
||||
|
||||
function genNode (node: ASTNode): string {
|
||||
function genNode (node: ASTNode, state: CodegenState): string {
|
||||
if (node.type === 1) {
|
||||
return genElement(node)
|
||||
return genElement(node, state)
|
||||
} if (node.type === 3 && node.isComment) {
|
||||
return genComment(node)
|
||||
} else {
|
||||
return genText(node)
|
||||
}
|
||||
}
|
||||
|
||||
function genText (text: ASTText | ASTExpression): string {
|
||||
export function genText (text: ASTText | ASTExpression): string {
|
||||
return `_v(${text.type === 2
|
||||
? text.expression // no need for () because already wrapped in _s()
|
||||
: transformSpecialNewlines(JSON.stringify(text.text))
|
||||
})`
|
||||
}
|
||||
|
||||
function genSlot (el: ASTElement): string {
|
||||
export function genComment (comment: ASTText): string {
|
||||
return `_e(${JSON.stringify(comment.text)})`
|
||||
}
|
||||
|
||||
function genSlot (el: ASTElement, state: CodegenState): string {
|
||||
const slotName = el.slotName || '"default"'
|
||||
const children = genChildren(el)
|
||||
const children = genChildren(el, state)
|
||||
let res = `_t(${slotName}${children ? `,${children}` : ''}`
|
||||
const attrs = el.attrs && `{${el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')}}`
|
||||
const bind = el.attrsMap['v-bind']
|
||||
@@ -405,9 +464,13 @@ function genSlot (el: ASTElement): string {
|
||||
}
|
||||
|
||||
// componentName is el.component, take it as argument to shun flow's pessimistic refinement
|
||||
function genComponent (componentName: string, el: ASTElement): string {
|
||||
const children = el.inlineTemplate ? null : genChildren(el, true)
|
||||
return `_c(${componentName},${genData(el)}${
|
||||
function genComponent (
|
||||
componentName: string,
|
||||
el: ASTElement,
|
||||
state: CodegenState
|
||||
): string {
|
||||
const children = el.inlineTemplate ? null : genChildren(el, state, true)
|
||||
return `_c(${componentName},${genData(el, state)}${
|
||||
children ? `,${children}` : ''
|
||||
})`
|
||||
}
|
||||
|
||||
+4
-2
@@ -2,8 +2,10 @@
|
||||
|
||||
export default function bind (el: ASTElement, dir: ASTDirective) {
|
||||
el.wrapData = (code: string) => {
|
||||
return `_b(${code},'${el.tag}',${dir.value}${
|
||||
dir.modifiers && dir.modifiers.prop ? ',true' : ''
|
||||
return `_b(${code},'${el.tag}',${dir.value},${
|
||||
dir.modifiers && dir.modifiers.prop ? 'true' : 'false'
|
||||
}${
|
||||
dir.modifiers && dir.modifiers.sync ? ',true' : ''
|
||||
})`
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,9 +1,11 @@
|
||||
/* @flow */
|
||||
|
||||
import on from './on'
|
||||
import bind from './bind'
|
||||
import { noop } from 'shared/util'
|
||||
|
||||
export default {
|
||||
on,
|
||||
bind,
|
||||
cloak: noop
|
||||
}
|
||||
|
||||
+1
-4
@@ -41,10 +41,7 @@ export function genAssignmentCode (
|
||||
if (modelRs.idx === null) {
|
||||
return `${value}=${assignment}`
|
||||
} else {
|
||||
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
|
||||
`if (!Array.isArray($$exp)){` +
|
||||
`${value}=${assignment}}` +
|
||||
`else{$$exp.splice($$idx, 1, ${assignment})}`
|
||||
return `$set(${modelRs.exp}, ${modelRs.idx}, ${assignment})`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-142
@@ -3,11 +3,12 @@
|
||||
import { parse } from './parser/index'
|
||||
import { optimize } from './optimizer'
|
||||
import { generate } from './codegen/index'
|
||||
import { detectErrors } from './error-detector'
|
||||
import { extend, noop } from 'shared/util'
|
||||
import { warn, tip } from 'core/util/debug'
|
||||
import { createCompilerCreator } from './create-compiler'
|
||||
|
||||
function baseCompile (
|
||||
// `createCompilerCreator` allows creating compilers that use alternative
|
||||
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
|
||||
// Here we just export a default compiler using the default parts.
|
||||
export const createCompiler = createCompilerCreator(function baseCompile (
|
||||
template: string,
|
||||
options: CompilerOptions
|
||||
): CompiledResult {
|
||||
@@ -19,141 +20,4 @@ function baseCompile (
|
||||
render: code.render,
|
||||
staticRenderFns: code.staticRenderFns
|
||||
}
|
||||
}
|
||||
|
||||
function makeFunction (code, errors) {
|
||||
try {
|
||||
return new Function(code)
|
||||
} catch (err) {
|
||||
errors.push({ err, code })
|
||||
return noop
|
||||
}
|
||||
}
|
||||
|
||||
export function createCompiler (baseOptions: CompilerOptions) {
|
||||
const functionCompileCache: {
|
||||
[key: string]: CompiledFunctionResult;
|
||||
} = Object.create(null)
|
||||
|
||||
function compile (
|
||||
template: string,
|
||||
options?: CompilerOptions
|
||||
): CompiledResult {
|
||||
const finalOptions = Object.create(baseOptions)
|
||||
const errors = []
|
||||
const tips = []
|
||||
finalOptions.warn = (msg, tip) => {
|
||||
(tip ? tips : errors).push(msg)
|
||||
}
|
||||
|
||||
if (options) {
|
||||
// merge custom modules
|
||||
if (options.modules) {
|
||||
finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
|
||||
}
|
||||
// merge custom directives
|
||||
if (options.directives) {
|
||||
finalOptions.directives = extend(
|
||||
Object.create(baseOptions.directives),
|
||||
options.directives
|
||||
)
|
||||
}
|
||||
// copy other options
|
||||
for (const key in options) {
|
||||
if (key !== 'modules' && key !== 'directives') {
|
||||
finalOptions[key] = options[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const compiled = baseCompile(template, finalOptions)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
errors.push.apply(errors, detectErrors(compiled.ast))
|
||||
}
|
||||
compiled.errors = errors
|
||||
compiled.tips = tips
|
||||
return compiled
|
||||
}
|
||||
|
||||
function compileToFunctions (
|
||||
template: string,
|
||||
options?: CompilerOptions,
|
||||
vm?: Component
|
||||
): CompiledFunctionResult {
|
||||
options = options || {}
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// detect possible CSP restriction
|
||||
try {
|
||||
new Function('return 1')
|
||||
} catch (e) {
|
||||
if (e.toString().match(/unsafe-eval|CSP/)) {
|
||||
warn(
|
||||
'It seems you are using the standalone build of Vue.js in an ' +
|
||||
'environment with Content Security Policy that prohibits unsafe-eval. ' +
|
||||
'The template compiler cannot work in this environment. Consider ' +
|
||||
'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
|
||||
'templates into render functions.'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check cache
|
||||
const key = options.delimiters
|
||||
? String(options.delimiters) + template
|
||||
: template
|
||||
if (functionCompileCache[key]) {
|
||||
return functionCompileCache[key]
|
||||
}
|
||||
|
||||
// compile
|
||||
const compiled = compile(template, options)
|
||||
|
||||
// check compilation errors/tips
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (compiled.errors && compiled.errors.length) {
|
||||
warn(
|
||||
`Error compiling template:\n\n${template}\n\n` +
|
||||
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
|
||||
vm
|
||||
)
|
||||
}
|
||||
if (compiled.tips && compiled.tips.length) {
|
||||
compiled.tips.forEach(msg => tip(msg, vm))
|
||||
}
|
||||
}
|
||||
|
||||
// turn code into functions
|
||||
const res = {}
|
||||
const fnGenErrors = []
|
||||
res.render = makeFunction(compiled.render, fnGenErrors)
|
||||
const l = compiled.staticRenderFns.length
|
||||
res.staticRenderFns = new Array(l)
|
||||
for (let i = 0; i < l; i++) {
|
||||
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors)
|
||||
}
|
||||
|
||||
// check function generation errors.
|
||||
// this should only happen if there is a bug in the compiler itself.
|
||||
// mostly for codegen development use
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
|
||||
warn(
|
||||
`Failed to generate render function:\n\n` +
|
||||
fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'),
|
||||
vm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (functionCompileCache[key] = res)
|
||||
}
|
||||
|
||||
return {
|
||||
compile,
|
||||
compileToFunctions
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+12
-7
@@ -55,6 +55,15 @@ function markStatic (node: ASTNode) {
|
||||
node.static = false
|
||||
}
|
||||
}
|
||||
if (node.ifConditions) {
|
||||
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
|
||||
const block = node.ifConditions[i].block
|
||||
markStatic(block)
|
||||
if (!block.static) {
|
||||
node.static = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,17 +90,13 @@ function markStaticRoots (node: ASTNode, isInFor: boolean) {
|
||||
}
|
||||
}
|
||||
if (node.ifConditions) {
|
||||
walkThroughConditionsBlocks(node.ifConditions, isInFor)
|
||||
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
|
||||
markStaticRoots(node.ifConditions[i].block, isInFor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function walkThroughConditionsBlocks (conditionBlocks: ASTIfConditions, isInFor: boolean): void {
|
||||
for (let i = 1, len = conditionBlocks.length; i < len; i++) {
|
||||
markStaticRoots(conditionBlocks[i].block, isInFor)
|
||||
}
|
||||
}
|
||||
|
||||
function isStatic (node: ASTNode): boolean {
|
||||
if (node.type === 2) { // expression
|
||||
return false
|
||||
|
||||
+6
-4
@@ -2,8 +2,10 @@
|
||||
|
||||
let decoder
|
||||
|
||||
export function decode (html: string): string {
|
||||
decoder = decoder || document.createElement('div')
|
||||
decoder.innerHTML = html
|
||||
return decoder.textContent
|
||||
export default {
|
||||
decode (html: string): string {
|
||||
decoder = decoder || document.createElement('div')
|
||||
decoder.innerHTML = html
|
||||
return decoder.textContent
|
||||
}
|
||||
}
|
||||
|
||||
+22
-24
@@ -13,29 +13,14 @@ import { makeMap, no } from 'shared/util'
|
||||
import { isNonPhrasingTag } from 'web/compiler/util'
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
const singleAttrIdentifier = /([^\s"'<>/=]+)/
|
||||
const singleAttrAssign = /(?:=)/
|
||||
const singleAttrValues = [
|
||||
// attr value double quotes
|
||||
/"([^"]*)"+/.source,
|
||||
// attr value, single quotes
|
||||
/'([^']*)'+/.source,
|
||||
// attr value, no quotes
|
||||
/([^\s"'=<>`]+)/.source
|
||||
]
|
||||
const attribute = new RegExp(
|
||||
'^\\s*' + singleAttrIdentifier.source +
|
||||
'(?:\\s*(' + singleAttrAssign.source + ')' +
|
||||
'\\s*(?:' + singleAttrValues.join('|') + '))?'
|
||||
)
|
||||
|
||||
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
|
||||
// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
|
||||
// but for Vue templates we can enforce a simple charset
|
||||
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
|
||||
const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')'
|
||||
const startTagOpen = new RegExp('^<' + qnameCapture)
|
||||
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
|
||||
const startTagOpen = new RegExp(`^<${qnameCapture}`)
|
||||
const startTagClose = /^\s*(\/?)>/
|
||||
const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>')
|
||||
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
|
||||
const doctype = /^<!DOCTYPE [^>]+>/i
|
||||
const comment = /^<!--/
|
||||
const conditionalComment = /^<!\[/
|
||||
@@ -59,6 +44,10 @@ const decodingMap = {
|
||||
const encodedAttr = /&(?:lt|gt|quot|amp);/g
|
||||
const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#10);/g
|
||||
|
||||
// #5992
|
||||
const isIgnoreNewlineTag = makeMap('pre,textarea', true)
|
||||
const shouldIgnoreFirstNewline = (tag, html) => tag && isIgnoreNewlineTag(tag) && html[0] === '\n'
|
||||
|
||||
function decodeAttr (value, shouldDecodeNewlines) {
|
||||
const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr
|
||||
return value.replace(re, match => decodingMap[match])
|
||||
@@ -82,6 +71,9 @@ export function parseHTML (html, options) {
|
||||
const commentEnd = html.indexOf('-->')
|
||||
|
||||
if (commentEnd >= 0) {
|
||||
if (options.shouldKeepComment) {
|
||||
options.comment(html.substring(4, commentEnd))
|
||||
}
|
||||
advance(commentEnd + 3)
|
||||
continue
|
||||
}
|
||||
@@ -117,6 +109,9 @@ export function parseHTML (html, options) {
|
||||
const startTagMatch = parseStartTag()
|
||||
if (startTagMatch) {
|
||||
handleStartTag(startTagMatch)
|
||||
if (shouldIgnoreFirstNewline(lastTag, html)) {
|
||||
advance(1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -149,16 +144,19 @@ export function parseHTML (html, options) {
|
||||
options.chars(text)
|
||||
}
|
||||
} else {
|
||||
var stackedTag = lastTag.toLowerCase()
|
||||
var reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'))
|
||||
var endTagLength = 0
|
||||
var rest = html.replace(reStackedTag, function (all, text, endTag) {
|
||||
let endTagLength = 0
|
||||
const stackedTag = lastTag.toLowerCase()
|
||||
const reStackedTag = reCache[stackedTag] || (reCache[stackedTag] = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i'))
|
||||
const rest = html.replace(reStackedTag, function (all, text, endTag) {
|
||||
endTagLength = endTag.length
|
||||
if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
|
||||
text = text
|
||||
.replace(/<!--([\s\S]*?)-->/g, '$1')
|
||||
.replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1')
|
||||
}
|
||||
if (shouldIgnoreFirstNewline(stackedTag, text)) {
|
||||
text = text.slice(1)
|
||||
}
|
||||
if (options.chars) {
|
||||
options.chars(text)
|
||||
}
|
||||
@@ -222,7 +220,7 @@ export function parseHTML (html, options) {
|
||||
}
|
||||
}
|
||||
|
||||
const unary = isUnaryTag(tagName) || tagName === 'html' && lastTag === 'head' || !!unarySlash
|
||||
const unary = isUnaryTag(tagName) || !!unarySlash
|
||||
|
||||
const l = match.attrs.length
|
||||
const attrs = new Array(l)
|
||||
|
||||
+21
-6
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { decode } from 'he'
|
||||
import he from 'he'
|
||||
import { parseHTML } from './html-parser'
|
||||
import { parseText } from './text-parser'
|
||||
import { parseFilters } from './filter-parser'
|
||||
@@ -28,7 +28,7 @@ const argRE = /:(.*)$/
|
||||
const bindRE = /^:|^v-bind:/
|
||||
const modifierRE = /\.[^.]+/g
|
||||
|
||||
const decodeHTMLCached = cached(decode)
|
||||
const decodeHTMLCached = cached(he.decode)
|
||||
|
||||
// configurable state
|
||||
export let warn
|
||||
@@ -48,12 +48,15 @@ export function parse (
|
||||
options: CompilerOptions
|
||||
): ASTElement | void {
|
||||
warn = options.warn || baseWarn
|
||||
platformGetTagNamespace = options.getTagNamespace || no
|
||||
platformMustUseProp = options.mustUseProp || no
|
||||
|
||||
platformIsPreTag = options.isPreTag || no
|
||||
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
|
||||
platformMustUseProp = options.mustUseProp || no
|
||||
platformGetTagNamespace = options.getTagNamespace || no
|
||||
|
||||
transforms = pluckModuleFunction(options.modules, 'transformNode')
|
||||
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
|
||||
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
|
||||
|
||||
delimiters = options.delimiters
|
||||
|
||||
const stack = []
|
||||
@@ -87,6 +90,7 @@ export function parse (
|
||||
isUnaryTag: options.isUnaryTag,
|
||||
canBeLeftOpenTag: options.canBeLeftOpenTag,
|
||||
shouldDecodeNewlines: options.shouldDecodeNewlines,
|
||||
shouldKeepComment: options.comments,
|
||||
start (tag, attrs, unary) {
|
||||
// check namespace.
|
||||
// inherit parent ns if there is one
|
||||
@@ -271,6 +275,13 @@ export function parse (
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
comment (text: string) {
|
||||
currentParent.children.push({
|
||||
type: 3,
|
||||
text,
|
||||
isComment: true
|
||||
})
|
||||
}
|
||||
})
|
||||
return root
|
||||
@@ -420,6 +431,8 @@ function processSlot (el) {
|
||||
const slotTarget = getBindingAttr(el, 'slot')
|
||||
if (slotTarget) {
|
||||
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
|
||||
// preserve slot as an attribute for native shadow DOM compat
|
||||
addAttr(el, 'slot', slotTarget)
|
||||
}
|
||||
if (el.tag === 'template') {
|
||||
el.slotScope = getAndRemoveAttr(el, 'scope')
|
||||
@@ -472,7 +485,9 @@ function processAttrs (el) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isProp || platformMustUseProp(el.tag, el.attrsMap.type, name)) {
|
||||
if (isProp || (
|
||||
!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
|
||||
)) {
|
||||
addProp(el, name, value)
|
||||
} else {
|
||||
addAttr(el, name, value)
|
||||
|
||||
+7
-5
@@ -5,14 +5,16 @@ import { getFirstComponentChild } from 'core/vdom/helpers/index'
|
||||
|
||||
type VNodeCache = { [key: string]: ?VNode };
|
||||
|
||||
const patternTypes: Array<Function> = [String, RegExp]
|
||||
const patternTypes: Array<Function> = [String, RegExp, Array]
|
||||
|
||||
function getComponentName (opts: ?VNodeComponentOptions): ?string {
|
||||
return opts && (opts.Ctor.options.name || opts.tag)
|
||||
}
|
||||
|
||||
function matches (pattern: string | RegExp, name: string): boolean {
|
||||
if (typeof pattern === 'string') {
|
||||
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
|
||||
if (Array.isArray(pattern)) {
|
||||
return pattern.indexOf(name) > -1
|
||||
} else if (typeof pattern === 'string') {
|
||||
return pattern.split(',').indexOf(name) > -1
|
||||
} else if (isRegExp(pattern)) {
|
||||
return pattern.test(name)
|
||||
@@ -62,10 +64,10 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
include (val: string | RegExp) {
|
||||
include (val: string | RegExp | Array<string>) {
|
||||
pruneCache(this.cache, this._vnode, name => matches(val, name))
|
||||
},
|
||||
exclude (val: string | RegExp) {
|
||||
exclude (val: string | RegExp | Array<string>) {
|
||||
pruneCache(this.cache, this._vnode, name => !matches(val, name))
|
||||
}
|
||||
},
|
||||
|
||||
+6
@@ -16,6 +16,7 @@ export type Config = {
|
||||
performance: boolean;
|
||||
devtools: boolean;
|
||||
errorHandler: ?(err: Error, vm: Component, info: string) => void;
|
||||
warnHandler: ?(msg: string, vm: Component, trace: string) => void;
|
||||
ignoredElements: Array<string>;
|
||||
keyCodes: { [key: string]: number | Array<number> };
|
||||
|
||||
@@ -62,6 +63,11 @@ export default ({
|
||||
*/
|
||||
errorHandler: null,
|
||||
|
||||
/**
|
||||
* Warn handler for watcher warns
|
||||
*/
|
||||
warnHandler: null,
|
||||
|
||||
/**
|
||||
* Ignore certain custom elements
|
||||
*/
|
||||
|
||||
+4
-3
@@ -4,10 +4,11 @@ import { toArray } from '../util/index'
|
||||
|
||||
export function initUse (Vue: GlobalAPI) {
|
||||
Vue.use = function (plugin: Function | Object) {
|
||||
/* istanbul ignore if */
|
||||
if (plugin.installed) {
|
||||
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
|
||||
if (installedPlugins.indexOf(plugin) > -1) {
|
||||
return this
|
||||
}
|
||||
|
||||
// additional parameters
|
||||
const args = toArray(arguments, 1)
|
||||
args.unshift(this)
|
||||
@@ -16,7 +17,7 @@ export function initUse (Vue: GlobalAPI) {
|
||||
} else if (typeof plugin === 'function') {
|
||||
plugin.apply(null, args)
|
||||
}
|
||||
plugin.installed = true
|
||||
installedPlugins.push(plugin)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ Object.defineProperty(Vue.prototype, '$isServer', {
|
||||
Object.defineProperty(Vue.prototype, '$ssrContext', {
|
||||
get () {
|
||||
/* istanbul ignore next */
|
||||
return this.$vnode.ssrContext
|
||||
return this.$vnode && this.$vnode.ssrContext
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
+22
-10
@@ -1,7 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import {
|
||||
tip,
|
||||
toArray,
|
||||
hyphenate,
|
||||
handleError,
|
||||
formatComponentName
|
||||
} from '../util/index'
|
||||
import { updateListeners } from '../vdom/helpers/index'
|
||||
import { toArray, tip, hyphenate, formatComponentName } from '../util/index'
|
||||
|
||||
export function initEvents (vm: Component) {
|
||||
vm._events = Object.create(null)
|
||||
@@ -89,14 +95,16 @@ export function eventsMixin (Vue: Class<Component>) {
|
||||
vm._events[event] = null
|
||||
return vm
|
||||
}
|
||||
// specific handler
|
||||
let cb
|
||||
let i = cbs.length
|
||||
while (i--) {
|
||||
cb = cbs[i]
|
||||
if (cb === fn || cb.fn === fn) {
|
||||
cbs.splice(i, 1)
|
||||
break
|
||||
if (fn) {
|
||||
// specific handler
|
||||
let cb
|
||||
let i = cbs.length
|
||||
while (i--) {
|
||||
cb = cbs[i]
|
||||
if (cb === fn || cb.fn === fn) {
|
||||
cbs.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return vm
|
||||
@@ -121,7 +129,11 @@ export function eventsMixin (Vue: Class<Component>) {
|
||||
cbs = cbs.length > 1 ? toArray(cbs) : cbs
|
||||
const args = toArray(arguments, 1)
|
||||
for (let i = 0, l = cbs.length; i < l; i++) {
|
||||
cbs[i].apply(vm, args)
|
||||
try {
|
||||
cbs[i].apply(vm, args)
|
||||
} catch (e) {
|
||||
handleError(e, vm, `event handler for "${event}"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vm
|
||||
|
||||
+13
-9
@@ -1,8 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import { hasSymbol } from 'core/util/env'
|
||||
import { warn } from '../util/index'
|
||||
import { defineReactive } from '../observer/index'
|
||||
import { hasSymbol } from 'core/util/env'
|
||||
import { defineReactive, observerState } from '../observer/index'
|
||||
|
||||
export function initProvide (vm: Component) {
|
||||
const provide = vm.$options.provide
|
||||
@@ -16,6 +16,7 @@ export function initProvide (vm: Component) {
|
||||
export function initInjections (vm: Component) {
|
||||
const result = resolveInject(vm.$options.inject, vm)
|
||||
if (result) {
|
||||
observerState.shouldConvert = false
|
||||
Object.keys(result).forEach(key => {
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@@ -31,24 +32,24 @@ export function initInjections (vm: Component) {
|
||||
defineReactive(vm, key, result[key])
|
||||
}
|
||||
})
|
||||
observerState.shouldConvert = true
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveInject (inject: any, vm: Component): ?Object {
|
||||
if (inject) {
|
||||
// inject is :any because flow is not smart enough to figure out cached
|
||||
// isArray here
|
||||
const isArray = Array.isArray(inject)
|
||||
const result = Object.create(null)
|
||||
const keys = isArray
|
||||
? inject
|
||||
: hasSymbol
|
||||
? Reflect.ownKeys(inject)
|
||||
const keys = hasSymbol
|
||||
? Reflect.ownKeys(inject).filter(key => {
|
||||
/* istanbul ignore next */
|
||||
return Object.getOwnPropertyDescriptor(inject, key).enumerable
|
||||
})
|
||||
: Object.keys(inject)
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
const provideKey = isArray ? key : inject[key]
|
||||
const provideKey = inject[key]
|
||||
let source = vm
|
||||
while (source) {
|
||||
if (source._provided && provideKey in source._provided) {
|
||||
@@ -57,6 +58,9 @@ export function resolveInject (inject: any, vm: Component): ?Object {
|
||||
}
|
||||
source = source.$parent
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production' && !source) {
|
||||
warn(`Injection "${key}" not found`, vm)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
+20
-8
@@ -18,6 +18,7 @@ import {
|
||||
} from '../util/index'
|
||||
|
||||
export let activeInstance: any = null
|
||||
export let isUpdatingChildComponent: boolean = false
|
||||
|
||||
export function initLifecycle (vm: Component) {
|
||||
const options = vm.$options
|
||||
@@ -65,6 +66,9 @@ export function lifecycleMixin (Vue: Class<Component>) {
|
||||
vm.$options._parentElm,
|
||||
vm.$options._refElm
|
||||
)
|
||||
// no need for the ref nodes after initial patch
|
||||
// this prevents keeping a detached DOM tree in memory (#5851)
|
||||
vm.$options._parentElm = vm.$options._refElm = null
|
||||
} else {
|
||||
// updates
|
||||
vm.$el = vm.__patch__(prevVnode, vnode)
|
||||
@@ -129,8 +133,6 @@ export function lifecycleMixin (Vue: Class<Component>) {
|
||||
if (vm.$el) {
|
||||
vm.$el.__vue__ = null
|
||||
}
|
||||
// remove reference to DOM nodes (prevents leak)
|
||||
vm.$options._parentElm = vm.$options._refElm = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +208,10 @@ export function updateChildComponent (
|
||||
parentVnode: VNode,
|
||||
renderChildren: ?Array<VNode>
|
||||
) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
isUpdatingChildComponent = true
|
||||
}
|
||||
|
||||
// determine whether component has slot children
|
||||
// we need to do this before overwriting $options._renderChildren
|
||||
const hasChildren = !!(
|
||||
@@ -217,17 +223,21 @@ export function updateChildComponent (
|
||||
|
||||
vm.$options._parentVnode = parentVnode
|
||||
vm.$vnode = parentVnode // update vm's placeholder node without re-render
|
||||
|
||||
if (vm._vnode) { // update child tree's parent
|
||||
vm._vnode.parent = parentVnode
|
||||
}
|
||||
vm.$options._renderChildren = renderChildren
|
||||
|
||||
// update $attrs and $listeners hash
|
||||
// these are also reactive so they may trigger child update if the child
|
||||
// used them during render
|
||||
vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject
|
||||
vm.$listeners = listeners || emptyObject
|
||||
|
||||
// update props
|
||||
if (propsData && vm.$options.props) {
|
||||
observerState.shouldConvert = false
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
observerState.isSettingProps = true
|
||||
}
|
||||
const props = vm._props
|
||||
const propKeys = vm.$options._propKeys || []
|
||||
for (let i = 0; i < propKeys.length; i++) {
|
||||
@@ -235,12 +245,10 @@ export function updateChildComponent (
|
||||
props[key] = validateProp(key, vm.$options.props, propsData, vm)
|
||||
}
|
||||
observerState.shouldConvert = true
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
observerState.isSettingProps = false
|
||||
}
|
||||
// keep a copy of raw propsData
|
||||
vm.$options.propsData = propsData
|
||||
}
|
||||
|
||||
// update listeners
|
||||
if (listeners) {
|
||||
const oldListeners = vm.$options._parentListeners
|
||||
@@ -252,6 +260,10 @@ export function updateChildComponent (
|
||||
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
|
||||
vm.$forceUpdate()
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
isUpdatingChildComponent = false
|
||||
}
|
||||
}
|
||||
|
||||
function isInInactiveTree (vm) {
|
||||
|
||||
+21
-3
@@ -1,7 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import config from 'core/config'
|
||||
import { isObject, warn, toObject } from 'core/util/index'
|
||||
|
||||
import {
|
||||
warn,
|
||||
isObject,
|
||||
toObject,
|
||||
isReservedAttribute
|
||||
} from 'core/util/index'
|
||||
|
||||
/**
|
||||
* Runtime helper for merging v-bind="object" into a VNode's data.
|
||||
@@ -10,7 +16,8 @@ export function bindObjectProps (
|
||||
data: any,
|
||||
tag: string,
|
||||
value: any,
|
||||
asProp?: boolean
|
||||
asProp: boolean,
|
||||
isSync?: boolean
|
||||
): VNodeData {
|
||||
if (value) {
|
||||
if (!isObject(value)) {
|
||||
@@ -24,7 +31,11 @@ export function bindObjectProps (
|
||||
}
|
||||
let hash
|
||||
for (const key in value) {
|
||||
if (key === 'class' || key === 'style') {
|
||||
if (
|
||||
key === 'class' ||
|
||||
key === 'style' ||
|
||||
isReservedAttribute(key)
|
||||
) {
|
||||
hash = data
|
||||
} else {
|
||||
const type = data.attrs && data.attrs.type
|
||||
@@ -34,6 +45,13 @@ export function bindObjectProps (
|
||||
}
|
||||
if (!(key in hash)) {
|
||||
hash[key] = value[key]
|
||||
|
||||
if (isSync) {
|
||||
const on = data.on || (data.on = {})
|
||||
on[`update:${key}`] = function ($event) {
|
||||
value[key] = $event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -7,7 +7,11 @@ import { isObject, isDef } from 'core/util/index'
|
||||
*/
|
||||
export function renderList (
|
||||
val: any,
|
||||
render: () => VNode
|
||||
render: (
|
||||
val: any,
|
||||
keyOrIndex: string | number,
|
||||
index?: number
|
||||
) => VNode
|
||||
): ?Array<VNode> {
|
||||
let ret: ?Array<VNode>, i, l, keys, key
|
||||
if (Array.isArray(val) || typeof val === 'string') {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ export function renderSlot (
|
||||
if (scopedSlotFn) { // scoped slot
|
||||
props = props || {}
|
||||
if (bindObject) {
|
||||
extend(props, bindObject)
|
||||
props = extend(extend({}, bindObject), props)
|
||||
}
|
||||
return scopedSlotFn(props) || fallback
|
||||
} else {
|
||||
|
||||
+6
-1
@@ -14,10 +14,15 @@ export function resolveSlots (
|
||||
const defaultSlot = []
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
const child = children[i]
|
||||
const data = child.data
|
||||
// remove slot attribute if the node is resolved as a Vue slot node
|
||||
if (data && data.attrs && data.attrs.slot) {
|
||||
delete data.attrs.slot
|
||||
}
|
||||
// named slots should only be respected if the vnode was rendered in the
|
||||
// same context.
|
||||
if ((child.context === context || child.functionalContext === context) &&
|
||||
child.data && child.data.slot != null
|
||||
data && data.slot != null
|
||||
) {
|
||||
const name = child.data.slot
|
||||
const slot = (slots[name] || (slots[name] = []))
|
||||
|
||||
+29
-3
@@ -8,7 +8,8 @@ import {
|
||||
looseEqual,
|
||||
emptyObject,
|
||||
handleError,
|
||||
looseIndexOf
|
||||
looseIndexOf,
|
||||
defineReactive
|
||||
} from '../util/index'
|
||||
|
||||
import VNode, {
|
||||
@@ -17,6 +18,8 @@ import VNode, {
|
||||
createEmptyVNode
|
||||
} from '../vdom/vnode'
|
||||
|
||||
import { isUpdatingChildComponent } from './lifecycle'
|
||||
|
||||
import { createElement } from '../vdom/create-element'
|
||||
import { renderList } from './render-helpers/render-list'
|
||||
import { renderSlot } from './render-helpers/render-slot'
|
||||
@@ -24,6 +27,7 @@ import { resolveFilter } from './render-helpers/resolve-filter'
|
||||
import { checkKeyCodes } from './render-helpers/check-keycodes'
|
||||
import { bindObjectProps } from './render-helpers/bind-object-props'
|
||||
import { renderStatic, markOnce } from './render-helpers/render-static'
|
||||
import { bindObjectListeners } from './render-helpers/bind-object-listeners'
|
||||
import { resolveSlots, resolveScopedSlots } from './render-helpers/resolve-slots'
|
||||
|
||||
export function initRender (vm: Component) {
|
||||
@@ -41,6 +45,23 @@ export function initRender (vm: Component) {
|
||||
// normalization is always applied for the public version, used in
|
||||
// user-written render functions.
|
||||
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
|
||||
|
||||
// $attrs & $listeners are exposed for easier HOC creation.
|
||||
// they need to be reactive so that HOCs using them are always updated
|
||||
const parentData = parentVnode && parentVnode.data
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
|
||||
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
|
||||
}, true)
|
||||
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, () => {
|
||||
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
|
||||
}, true)
|
||||
} else {
|
||||
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
|
||||
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, null, true)
|
||||
}
|
||||
}
|
||||
|
||||
export function renderMixin (Vue: Class<Component>) {
|
||||
@@ -57,9 +78,13 @@ export function renderMixin (Vue: Class<Component>) {
|
||||
} = vm.$options
|
||||
|
||||
if (vm._isMounted) {
|
||||
// clone slot nodes on re-renders
|
||||
// if the parent didn't update, the slot nodes will be the ones from
|
||||
// last render. They need to be cloned to ensure "freshness" for this render.
|
||||
for (const key in vm.$slots) {
|
||||
vm.$slots[key] = cloneVNodes(vm.$slots[key])
|
||||
const slot = vm.$slots[key]
|
||||
if (slot._rendered) {
|
||||
vm.$slots[key] = cloneVNodes(slot, true /* deep */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +146,5 @@ export function renderMixin (Vue: Class<Component>) {
|
||||
Vue.prototype._v = createTextVNode
|
||||
Vue.prototype._e = createEmptyVNode
|
||||
Vue.prototype._u = resolveScopedSlots
|
||||
Vue.prototype._g = bindObjectListeners
|
||||
}
|
||||
|
||||
+92
-33
@@ -3,6 +3,7 @@
|
||||
import config from '../config'
|
||||
import Dep from '../observer/dep'
|
||||
import Watcher from '../observer/watcher'
|
||||
import { isUpdatingChildComponent } from './lifecycle'
|
||||
|
||||
import {
|
||||
set,
|
||||
@@ -19,8 +20,11 @@ import {
|
||||
hasOwn,
|
||||
isReserved,
|
||||
handleError,
|
||||
nativeWatch,
|
||||
validateProp,
|
||||
isPlainObject
|
||||
isPlainObject,
|
||||
isServerRendering,
|
||||
isReservedAttribute
|
||||
} from '../util/index'
|
||||
|
||||
const sharedPropertyDefinition = {
|
||||
@@ -51,13 +55,19 @@ export function initState (vm: Component) {
|
||||
observe(vm._data = {}, true /* asRootData */)
|
||||
}
|
||||
if (opts.computed) initComputed(vm, opts.computed)
|
||||
if (opts.watch) initWatch(vm, opts.watch)
|
||||
if (opts.watch && opts.watch !== nativeWatch) {
|
||||
initWatch(vm, opts.watch)
|
||||
}
|
||||
}
|
||||
|
||||
const isReservedProp = {
|
||||
key: 1,
|
||||
ref: 1,
|
||||
slot: 1
|
||||
function checkOptionType (vm: Component, name: string) {
|
||||
const option = vm.$options[name]
|
||||
if (!isPlainObject(option)) {
|
||||
warn(
|
||||
`component option "${name}" should be an object.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function initProps (vm: Component, propsOptions: Object) {
|
||||
@@ -74,14 +84,14 @@ function initProps (vm: Component, propsOptions: Object) {
|
||||
const value = validateProp(key, propsOptions, propsData, vm)
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (isReservedProp[key] || config.isReservedAttr(key)) {
|
||||
if (isReservedAttribute(key) || config.isReservedAttr(key)) {
|
||||
warn(
|
||||
`"${key}" is a reserved attribute and cannot be used as component prop.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
defineReactive(props, key, value, () => {
|
||||
if (vm.$parent && !observerState.isSettingProps) {
|
||||
if (vm.$parent && !isUpdatingChildComponent) {
|
||||
warn(
|
||||
`Avoid mutating a prop directly since the value will be ` +
|
||||
`overwritten whenever the parent component re-renders. ` +
|
||||
@@ -120,16 +130,26 @@ function initData (vm: Component) {
|
||||
// proxy data on instance
|
||||
const keys = Object.keys(data)
|
||||
const props = vm.$options.props
|
||||
const methods = vm.$options.methods
|
||||
let i = keys.length
|
||||
while (i--) {
|
||||
if (props && hasOwn(props, keys[i])) {
|
||||
const key = keys[i]
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (methods && hasOwn(methods, key)) {
|
||||
warn(
|
||||
`Method "${key}" has already been defined as a data property.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
}
|
||||
if (props && hasOwn(props, key)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`The data property "${keys[i]}" is already declared as a prop. ` +
|
||||
`The data property "${key}" is already declared as a prop. ` +
|
||||
`Use prop default value instead.`,
|
||||
vm
|
||||
)
|
||||
} else if (!isReserved(keys[i])) {
|
||||
proxy(vm, `_data`, keys[i])
|
||||
} else if (!isReserved(key)) {
|
||||
proxy(vm, `_data`, key)
|
||||
}
|
||||
}
|
||||
// observe data
|
||||
@@ -148,22 +168,30 @@ function getData (data: Function, vm: Component): any {
|
||||
const computedWatcherOptions = { lazy: true }
|
||||
|
||||
function initComputed (vm: Component, computed: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'computed')
|
||||
const watchers = vm._computedWatchers = Object.create(null)
|
||||
// computed properties are just getters during SSR
|
||||
const isSSR = isServerRendering()
|
||||
|
||||
for (const key in computed) {
|
||||
const userDef = computed[key]
|
||||
let getter = typeof userDef === 'function' ? userDef : userDef.get
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (getter === undefined) {
|
||||
warn(
|
||||
`No getter function has been defined for computed property "${key}".`,
|
||||
vm
|
||||
)
|
||||
getter = noop
|
||||
}
|
||||
const getter = typeof userDef === 'function' ? userDef : userDef.get
|
||||
if (process.env.NODE_ENV !== 'production' && getter == null) {
|
||||
warn(
|
||||
`Getter is missing for computed property "${key}".`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
|
||||
if (!isSSR) {
|
||||
// create internal watcher for the computed property.
|
||||
watchers[key] = new Watcher(
|
||||
vm,
|
||||
getter || noop,
|
||||
noop,
|
||||
computedWatcherOptions
|
||||
)
|
||||
}
|
||||
// create internal watcher for the computed property.
|
||||
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
|
||||
|
||||
// component-defined computed properties are already defined on the
|
||||
// component prototype. We only need to define computed properties defined
|
||||
@@ -180,13 +208,20 @@ function initComputed (vm: Component, computed: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
export function defineComputed (target: any, key: string, userDef: Object | Function) {
|
||||
export function defineComputed (
|
||||
target: any,
|
||||
key: string,
|
||||
userDef: Object | Function
|
||||
) {
|
||||
const shouldCache = !isServerRendering()
|
||||
if (typeof userDef === 'function') {
|
||||
sharedPropertyDefinition.get = createComputedGetter(key)
|
||||
sharedPropertyDefinition.get = shouldCache
|
||||
? createComputedGetter(key)
|
||||
: userDef
|
||||
sharedPropertyDefinition.set = noop
|
||||
} else {
|
||||
sharedPropertyDefinition.get = userDef.get
|
||||
? userDef.cache !== false
|
||||
? shouldCache && userDef.cache !== false
|
||||
? createComputedGetter(key)
|
||||
: userDef.get
|
||||
: noop
|
||||
@@ -194,6 +229,15 @@ export function defineComputed (target: any, key: string, userDef: Object | Func
|
||||
? userDef.set
|
||||
: noop
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
sharedPropertyDefinition.set === noop) {
|
||||
sharedPropertyDefinition.set = function () {
|
||||
warn(
|
||||
`Computed property "${key}" was assigned to but it has no setter.`,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
Object.defineProperty(target, key, sharedPropertyDefinition)
|
||||
}
|
||||
|
||||
@@ -213,28 +257,36 @@ function createComputedGetter (key) {
|
||||
}
|
||||
|
||||
function initMethods (vm: Component, methods: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'methods')
|
||||
const props = vm.$options.props
|
||||
for (const key in methods) {
|
||||
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (methods[key] == null) {
|
||||
warn(
|
||||
`method "${key}" has an undefined value in the component definition. ` +
|
||||
`Method "${key}" has an undefined value in the component definition. ` +
|
||||
`Did you reference the function correctly?`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
if (props && hasOwn(props, key)) {
|
||||
warn(
|
||||
`method "${key}" has already been defined as a prop.`,
|
||||
`Method "${key}" has already been defined as a prop.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
if ((key in vm) && isReserved(key)) {
|
||||
warn(
|
||||
`Method "${key}" conflicts with an existing Vue instance method. ` +
|
||||
`Avoid defining component methods that start with _ or $.`
|
||||
)
|
||||
}
|
||||
}
|
||||
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
|
||||
}
|
||||
}
|
||||
|
||||
function initWatch (vm: Component, watch: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'watch')
|
||||
for (const key in watch) {
|
||||
const handler = watch[key]
|
||||
if (Array.isArray(handler)) {
|
||||
@@ -247,8 +299,12 @@ function initWatch (vm: Component, watch: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
function createWatcher (vm: Component, key: string, handler: any) {
|
||||
let options
|
||||
function createWatcher (
|
||||
vm: Component,
|
||||
keyOrFn: string | Function,
|
||||
handler: any,
|
||||
options?: Object
|
||||
) {
|
||||
if (isPlainObject(handler)) {
|
||||
options = handler
|
||||
handler = handler.handler
|
||||
@@ -256,7 +312,7 @@ function createWatcher (vm: Component, key: string, handler: any) {
|
||||
if (typeof handler === 'string') {
|
||||
handler = vm[handler]
|
||||
}
|
||||
vm.$watch(key, handler, options)
|
||||
return vm.$watch(keyOrFn, handler, options)
|
||||
}
|
||||
|
||||
export function stateMixin (Vue: Class<Component>) {
|
||||
@@ -287,10 +343,13 @@ export function stateMixin (Vue: Class<Component>) {
|
||||
|
||||
Vue.prototype.$watch = function (
|
||||
expOrFn: string | Function,
|
||||
cb: Function,
|
||||
cb: any,
|
||||
options?: Object
|
||||
): Function {
|
||||
const vm: Component = this
|
||||
if (isPlainObject(cb)) {
|
||||
return createWatcher(vm, expOrFn, cb, options)
|
||||
}
|
||||
options = options || {}
|
||||
options.user = true
|
||||
const watcher = new Watcher(vm, expOrFn, cb, options)
|
||||
|
||||
+1
-10
@@ -23,21 +23,12 @@ export const arrayMethods = Object.create(arrayProto)
|
||||
.forEach(function (method) {
|
||||
// cache original method
|
||||
const original = arrayProto[method]
|
||||
def(arrayMethods, method, function mutator () {
|
||||
// avoid leaking arguments:
|
||||
// http://jsperf.com/closure-with-arguments
|
||||
let i = arguments.length
|
||||
const args = new Array(i)
|
||||
while (i--) {
|
||||
args[i] = arguments[i]
|
||||
}
|
||||
def(arrayMethods, method, function mutator (...args) {
|
||||
const result = original.apply(this, args)
|
||||
const ob = this.__ob__
|
||||
let inserted
|
||||
switch (method) {
|
||||
case 'push':
|
||||
inserted = args
|
||||
break
|
||||
case 'unshift':
|
||||
inserted = args
|
||||
break
|
||||
|
||||
+17
-16
@@ -4,11 +4,12 @@ import Dep from './dep'
|
||||
import { arrayMethods } from './array'
|
||||
import {
|
||||
def,
|
||||
warn,
|
||||
hasOwn,
|
||||
hasProto,
|
||||
isObject,
|
||||
isPlainObject,
|
||||
hasProto,
|
||||
hasOwn,
|
||||
warn,
|
||||
isValidArrayIndex,
|
||||
isServerRendering
|
||||
} from '../util/index'
|
||||
|
||||
@@ -21,8 +22,7 @@ const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
||||
* under a frozen data structure. Converting it would defeat the optimization.
|
||||
*/
|
||||
export const observerState = {
|
||||
shouldConvert: true,
|
||||
isSettingProps: false
|
||||
shouldConvert: true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ export class Observer {
|
||||
* Augment an target Object or Array by intercepting
|
||||
* the prototype chain using __proto__
|
||||
*/
|
||||
function protoAugment (target, src: Object) {
|
||||
function protoAugment (target, src: Object, keys: any) {
|
||||
/* eslint-disable no-proto */
|
||||
target.__proto__ = src
|
||||
/* eslint-enable no-proto */
|
||||
@@ -132,7 +132,8 @@ export function defineReactive (
|
||||
obj: Object,
|
||||
key: string,
|
||||
val: any,
|
||||
customSetter?: Function
|
||||
customSetter?: ?Function,
|
||||
shallow?: boolean
|
||||
) {
|
||||
const dep = new Dep()
|
||||
|
||||
@@ -145,7 +146,7 @@ export function defineReactive (
|
||||
const getter = property && property.get
|
||||
const setter = property && property.set
|
||||
|
||||
let childOb = observe(val)
|
||||
let childOb = !shallow && observe(val)
|
||||
Object.defineProperty(obj, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@@ -155,9 +156,9 @@ export function defineReactive (
|
||||
dep.depend()
|
||||
if (childOb) {
|
||||
childOb.dep.depend()
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
dependArray(value)
|
||||
if (Array.isArray(value)) {
|
||||
dependArray(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
@@ -177,7 +178,7 @@ export function defineReactive (
|
||||
} else {
|
||||
val = newVal
|
||||
}
|
||||
childOb = observe(newVal)
|
||||
childOb = !shallow && observe(newVal)
|
||||
dep.notify()
|
||||
}
|
||||
})
|
||||
@@ -189,7 +190,7 @@ export function defineReactive (
|
||||
* already exist.
|
||||
*/
|
||||
export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
if (Array.isArray(target) && typeof key === 'number') {
|
||||
if (Array.isArray(target) && isValidArrayIndex(key)) {
|
||||
target.length = Math.max(target.length, key)
|
||||
target.splice(key, 1, val)
|
||||
return val
|
||||
@@ -198,7 +199,7 @@ export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
target[key] = val
|
||||
return val
|
||||
}
|
||||
const ob = (target : any).__ob__
|
||||
const ob = (target: any).__ob__
|
||||
if (target._isVue || (ob && ob.vmCount)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'Avoid adding reactive properties to a Vue instance or its root $data ' +
|
||||
@@ -219,11 +220,11 @@ export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
* Delete a property and trigger change if necessary.
|
||||
*/
|
||||
export function del (target: Array<any> | Object, key: any) {
|
||||
if (Array.isArray(target) && typeof key === 'number') {
|
||||
if (Array.isArray(target) && isValidArrayIndex(key)) {
|
||||
target.splice(key, 1)
|
||||
return
|
||||
}
|
||||
const ob = (target : any).__ob__
|
||||
const ob = (target: any).__ob__
|
||||
if (target._isVue || (ob && ob.vmCount)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'Avoid deleting properties on a Vue instance or its root $data ' +
|
||||
|
||||
+2
-2
@@ -81,7 +81,7 @@ function flushSchedulerQueue () {
|
||||
|
||||
// call component updated and activated hooks
|
||||
callActivatedHooks(activatedQueue)
|
||||
callUpdateHooks(updatedQueue)
|
||||
callUpdatedHooks(updatedQueue)
|
||||
|
||||
// devtool hook
|
||||
/* istanbul ignore if */
|
||||
@@ -90,7 +90,7 @@ function flushSchedulerQueue () {
|
||||
}
|
||||
}
|
||||
|
||||
function callUpdateHooks (queue) {
|
||||
function callUpdatedHooks (queue) {
|
||||
let i = queue.length
|
||||
while (i--) {
|
||||
const watcher = queue[i]
|
||||
|
||||
+15
-14
@@ -94,22 +94,23 @@ export default class Watcher {
|
||||
pushTarget(this)
|
||||
let value
|
||||
const vm = this.vm
|
||||
if (this.user) {
|
||||
try {
|
||||
value = this.getter.call(vm, vm)
|
||||
} catch (e) {
|
||||
handleError(e, vm, `getter for watcher "${this.expression}"`)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
value = this.getter.call(vm, vm)
|
||||
} catch (e) {
|
||||
if (this.user) {
|
||||
handleError(e, vm, `getter for watcher "${this.expression}"`)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
} finally {
|
||||
// "touch" every property so they are all tracked as
|
||||
// dependencies for deep watching
|
||||
if (this.deep) {
|
||||
traverse(value)
|
||||
}
|
||||
popTarget()
|
||||
this.cleanupDeps()
|
||||
}
|
||||
// "touch" every property so they are all tracked as
|
||||
// dependencies for deep watching
|
||||
if (this.deep) {
|
||||
traverse(value)
|
||||
}
|
||||
popTarget()
|
||||
this.cleanupDeps()
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -15,10 +15,12 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
.replace(/[-_]/g, '')
|
||||
|
||||
warn = (msg, vm) => {
|
||||
if (hasConsole && (!config.silent)) {
|
||||
console.error(`[Vue warn]: ${msg}` + (
|
||||
vm ? generateComponentTrace(vm) : ''
|
||||
))
|
||||
const trace = vm ? generateComponentTrace(vm) : ''
|
||||
|
||||
if (config.warnHandler) {
|
||||
config.warnHandler.call(null, msg, vm, trace)
|
||||
} else if (hasConsole && (!config.silent)) {
|
||||
console.error(`[Vue warn]: ${msg}${trace}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -17,6 +17,9 @@ export const isAndroid = UA && UA.indexOf('android') > 0
|
||||
export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
|
||||
export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
|
||||
|
||||
// Firefox has a "watch" function on Object.prototype...
|
||||
export const nativeWatch = ({}).watch
|
||||
|
||||
export let supportsPassive = false
|
||||
if (inBrowser) {
|
||||
try {
|
||||
@@ -26,7 +29,7 @@ if (inBrowser) {
|
||||
/* istanbul ignore next */
|
||||
supportsPassive = true
|
||||
}
|
||||
} : Object)) // https://github.com/facebook/flow/issues/285
|
||||
}: Object)) // https://github.com/facebook/flow/issues/285
|
||||
window.addEventListener('test-passive', null, opts)
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -96,13 +99,13 @@ export const nextTick = (function () {
|
||||
// "force" the microtask queue to be flushed by adding an empty timer.
|
||||
if (isIOS) setTimeout(noop)
|
||||
}
|
||||
} else if (typeof MutationObserver !== 'undefined' && (
|
||||
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
|
||||
isNative(MutationObserver) ||
|
||||
// PhantomJS and iOS 7.x
|
||||
MutationObserver.toString() === '[object MutationObserverConstructor]'
|
||||
)) {
|
||||
// use MutationObserver where native Promise is not available,
|
||||
// e.g. PhantomJS IE11, iOS7, Android 4.4
|
||||
// e.g. PhantomJS, iOS7, Android 4.4
|
||||
var counter = 1
|
||||
var observer = new MutationObserver(nextTickHandler)
|
||||
var textNode = document.createTextNode(String(counter))
|
||||
|
||||
+48
-16
@@ -2,6 +2,7 @@
|
||||
|
||||
import config from '../config'
|
||||
import { warn } from './debug'
|
||||
import { nativeWatch } from './env'
|
||||
import { set } from '../observer/index'
|
||||
|
||||
import {
|
||||
@@ -63,7 +64,7 @@ function mergeData (to: Object, from: ?Object): Object {
|
||||
/**
|
||||
* Data
|
||||
*/
|
||||
strats.data = function (
|
||||
export function mergeDataOrFn (
|
||||
parentVal: any,
|
||||
childVal: any,
|
||||
vm?: Component
|
||||
@@ -73,15 +74,6 @@ strats.data = function (
|
||||
if (!childVal) {
|
||||
return parentVal
|
||||
}
|
||||
if (typeof childVal !== 'function') {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'The "data" option should be a function ' +
|
||||
'that returns a per-instance value in component ' +
|
||||
'definitions.',
|
||||
vm
|
||||
)
|
||||
return parentVal
|
||||
}
|
||||
if (!parentVal) {
|
||||
return childVal
|
||||
}
|
||||
@@ -92,8 +84,8 @@ strats.data = function (
|
||||
// it has to be a function to pass previous merges.
|
||||
return function mergedDataFn () {
|
||||
return mergeData(
|
||||
childVal.call(this),
|
||||
parentVal.call(this)
|
||||
typeof childVal === 'function' ? childVal.call(this) : childVal,
|
||||
typeof parentVal === 'function' ? parentVal.call(this) : parentVal
|
||||
)
|
||||
}
|
||||
} else if (parentVal || childVal) {
|
||||
@@ -104,7 +96,7 @@ strats.data = function (
|
||||
: childVal
|
||||
const defaultData = typeof parentVal === 'function'
|
||||
? parentVal.call(vm)
|
||||
: undefined
|
||||
: parentVal
|
||||
if (instanceData) {
|
||||
return mergeData(instanceData, defaultData)
|
||||
} else {
|
||||
@@ -114,6 +106,28 @@ strats.data = function (
|
||||
}
|
||||
}
|
||||
|
||||
strats.data = function (
|
||||
parentVal: any,
|
||||
childVal: any,
|
||||
vm?: Component
|
||||
): ?Function {
|
||||
if (!vm) {
|
||||
if (childVal && typeof childVal !== 'function') {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'The "data" option should be a function ' +
|
||||
'that returns a per-instance value in component ' +
|
||||
'definitions.',
|
||||
vm
|
||||
)
|
||||
|
||||
return parentVal
|
||||
}
|
||||
return mergeDataOrFn.call(this, parentVal, childVal)
|
||||
}
|
||||
|
||||
return mergeDataOrFn(parentVal, childVal, vm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks and props are merged as arrays.
|
||||
*/
|
||||
@@ -159,6 +173,9 @@ ASSET_TYPES.forEach(function (type) {
|
||||
* another, so we merge them as arrays.
|
||||
*/
|
||||
strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
// work around Firefox's Object.prototype.watch...
|
||||
if (parentVal === nativeWatch) parentVal = undefined
|
||||
if (childVal === nativeWatch) childVal = undefined
|
||||
/* istanbul ignore if */
|
||||
if (!childVal) return Object.create(parentVal || null)
|
||||
if (!parentVal) return childVal
|
||||
@@ -172,7 +189,7 @@ strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
}
|
||||
ret[key] = parent
|
||||
? parent.concat(child)
|
||||
: [child]
|
||||
: Array.isArray(child) ? child : [child]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -182,14 +199,15 @@ strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
*/
|
||||
strats.props =
|
||||
strats.methods =
|
||||
strats.inject =
|
||||
strats.computed = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
if (!childVal) return Object.create(parentVal || null)
|
||||
if (!parentVal) return childVal
|
||||
const ret = Object.create(null)
|
||||
extend(ret, parentVal)
|
||||
extend(ret, childVal)
|
||||
if (childVal) extend(ret, childVal)
|
||||
return ret
|
||||
}
|
||||
strats.provide = mergeDataOrFn
|
||||
|
||||
/**
|
||||
* Default strategy.
|
||||
@@ -247,6 +265,19 @@ function normalizeProps (options: Object) {
|
||||
options.props = res
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize all injections into Object-based format
|
||||
*/
|
||||
function normalizeInject (options: Object) {
|
||||
const inject = options.inject
|
||||
if (Array.isArray(inject)) {
|
||||
const normalized = options.inject = {}
|
||||
for (let i = 0; i < inject.length; i++) {
|
||||
normalized[inject[i]] = inject[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize raw function directives into object format.
|
||||
*/
|
||||
@@ -280,6 +311,7 @@ export function mergeOptions (
|
||||
}
|
||||
|
||||
normalizeProps(child)
|
||||
normalizeInject(child)
|
||||
normalizeDirectives(child)
|
||||
const extendsFrom = child.extends
|
||||
if (extendsFrom) {
|
||||
|
||||
+14
-3
@@ -1,8 +1,14 @@
|
||||
/* @flow */
|
||||
|
||||
import { hasOwn, isObject, isPlainObject, capitalize, hyphenate } from 'shared/util'
|
||||
import { observe, observerState } from '../observer/index'
|
||||
import { warn } from './debug'
|
||||
import { observe, observerState } from '../observer/index'
|
||||
import {
|
||||
hasOwn,
|
||||
isObject,
|
||||
hyphenate,
|
||||
capitalize,
|
||||
isPlainObject
|
||||
} from 'shared/util'
|
||||
|
||||
type PropOptions = {
|
||||
type: Function | Array<Function> | null,
|
||||
@@ -139,7 +145,12 @@ function assertType (value: any, type: Function): {
|
||||
let valid
|
||||
const expectedType = getType(type)
|
||||
if (simpleCheckRE.test(expectedType)) {
|
||||
valid = typeof value === expectedType.toLowerCase()
|
||||
const t = typeof value
|
||||
valid = t === expectedType.toLowerCase()
|
||||
// for primitive wrapper objects
|
||||
if (!valid && t === 'object') {
|
||||
valid = value instanceof type
|
||||
}
|
||||
} else if (expectedType === 'Object') {
|
||||
valid = isPlainObject(value)
|
||||
} else if (expectedType === 'Array') {
|
||||
|
||||
+27
-9
@@ -15,6 +15,7 @@ import {
|
||||
|
||||
import {
|
||||
resolveAsyncComponent,
|
||||
createAsyncPlaceholder,
|
||||
extractPropsFromVNodeData
|
||||
} from './helpers/index'
|
||||
|
||||
@@ -97,7 +98,7 @@ const hooksToMerge = Object.keys(componentVNodeHooks)
|
||||
|
||||
export function createComponent (
|
||||
Ctor: Class<Component> | Function | Object | void,
|
||||
data?: VNodeData,
|
||||
data: ?VNodeData,
|
||||
context: Component,
|
||||
children: ?Array<VNode>,
|
||||
tag?: string
|
||||
@@ -123,21 +124,30 @@ export function createComponent (
|
||||
}
|
||||
|
||||
// async component
|
||||
let asyncFactory
|
||||
if (isUndef(Ctor.cid)) {
|
||||
Ctor = resolveAsyncComponent(Ctor, baseCtor, context)
|
||||
asyncFactory = Ctor
|
||||
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
|
||||
if (Ctor === undefined) {
|
||||
// return nothing if this is indeed an async component
|
||||
// wait for the callback to trigger parent update.
|
||||
return
|
||||
// return a placeholder node for async component, which is rendered
|
||||
// as a comment node but preserves all the raw information for the node.
|
||||
// the information will be used for async server-rendering and hydration.
|
||||
return createAsyncPlaceholder(
|
||||
asyncFactory,
|
||||
data,
|
||||
context,
|
||||
children,
|
||||
tag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data = data || {}
|
||||
|
||||
// resolve constructor options in case global mixins are applied after
|
||||
// component constructor creation
|
||||
resolveConstructorOptions(Ctor)
|
||||
|
||||
data = data || {}
|
||||
|
||||
// transform component v-model data into props & events
|
||||
if (isDef(data.model)) {
|
||||
transformModel(Ctor.options, data)
|
||||
@@ -155,12 +165,19 @@ export function createComponent (
|
||||
// child component listeners instead of DOM listeners
|
||||
const listeners = data.on
|
||||
// replace with listeners with .native modifier
|
||||
// so it gets processed during parent component patch.
|
||||
data.on = data.nativeOn
|
||||
|
||||
if (isTrue(Ctor.options.abstract)) {
|
||||
// abstract components do not keep anything
|
||||
// other than props & listeners
|
||||
// other than props & listeners & slot
|
||||
|
||||
// work around flow
|
||||
const slot = data.slot
|
||||
data = {}
|
||||
if (slot) {
|
||||
data.slot = slot
|
||||
}
|
||||
}
|
||||
|
||||
// merge component management hooks onto the placeholder node
|
||||
@@ -171,7 +188,8 @@ export function createComponent (
|
||||
const vnode = new VNode(
|
||||
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
|
||||
data, undefined, undefined, undefined, context,
|
||||
{ Ctor, propsData, listeners, tag, children }
|
||||
{ Ctor, propsData, listeners, tag, children },
|
||||
asyncFactory
|
||||
)
|
||||
return vnode
|
||||
}
|
||||
|
||||
+15
-1
@@ -57,10 +57,24 @@ export function _createElement (
|
||||
)
|
||||
return createEmptyVNode()
|
||||
}
|
||||
// object syntax in v-bind
|
||||
if (isDef(data) && isDef(data.is)) {
|
||||
tag = data.is
|
||||
}
|
||||
if (!tag) {
|
||||
// in case of component :is set to falsy value
|
||||
return createEmptyVNode()
|
||||
}
|
||||
// warn against non-primitive key
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
isDef(data) && isDef(data.key) && !isPrimitive(data.key)
|
||||
) {
|
||||
warn(
|
||||
'Avoid using non-primitive value as key, ' +
|
||||
'use string/number value instead.',
|
||||
context
|
||||
)
|
||||
}
|
||||
// support single function children as default scoped slot
|
||||
if (Array.isArray(children) &&
|
||||
typeof children[0] === 'function'
|
||||
@@ -77,7 +91,7 @@ export function _createElement (
|
||||
let vnode, ns
|
||||
if (typeof tag === 'string') {
|
||||
let Ctor
|
||||
ns = config.getTagNamespace(tag)
|
||||
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
|
||||
if (config.isReservedTag(tag)) {
|
||||
// platform built-in elements
|
||||
vnode = new VNode(
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@ import { resolveSlots } from '../instance/render-helpers/resolve-slots'
|
||||
import {
|
||||
isDef,
|
||||
camelize,
|
||||
emptyObject,
|
||||
validateProp
|
||||
} from '../util/index'
|
||||
|
||||
@@ -22,7 +23,7 @@ export function createFunctionalComponent (
|
||||
const propOptions = Ctor.options.props
|
||||
if (isDef(propOptions)) {
|
||||
for (const key in propOptions) {
|
||||
props[key] = validateProp(key, propOptions, propsData || {})
|
||||
props[key] = validateProp(key, propOptions, propsData || emptyObject)
|
||||
}
|
||||
} else {
|
||||
if (isDef(data.attrs)) mergeProps(props, data.attrs)
|
||||
@@ -37,7 +38,7 @@ export function createFunctionalComponent (
|
||||
props,
|
||||
children,
|
||||
parent: context,
|
||||
listeners: data.on || {},
|
||||
listeners: data.on || emptyObject,
|
||||
injections: resolveInject(Ctor.options.inject, context),
|
||||
slots: () => resolveSlots(children, context)
|
||||
})
|
||||
|
||||
+2
-1
@@ -1,12 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import { isDef } from 'shared/util'
|
||||
import { isAsyncPlaceholder } from './is-async-placeholder'
|
||||
|
||||
export function getFirstComponentChild (children: ?Array<VNode>): ?VNode {
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const c = children[i]
|
||||
if (isDef(c) && isDef(c.componentOptions)) {
|
||||
if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -6,3 +6,4 @@ export * from './update-listeners'
|
||||
export * from './normalize-children'
|
||||
export * from './resolve-async-component'
|
||||
export * from './get-first-component-child'
|
||||
export * from './is-async-placeholder'
|
||||
|
||||
+18
@@ -9,12 +9,30 @@ import {
|
||||
isObject
|
||||
} from 'core/util/index'
|
||||
|
||||
import { createEmptyVNode } from 'core/vdom/vnode'
|
||||
|
||||
function ensureCtor (comp, base) {
|
||||
if (comp.__esModule && comp.default) {
|
||||
comp = comp.default
|
||||
}
|
||||
return isObject(comp)
|
||||
? base.extend(comp)
|
||||
: comp
|
||||
}
|
||||
|
||||
export function createAsyncPlaceholder (
|
||||
factory: Function,
|
||||
data: ?VNodeData,
|
||||
context: Component,
|
||||
children: ?Array<VNode>,
|
||||
tag: ?string
|
||||
): VNode {
|
||||
const node = createEmptyVNode()
|
||||
node.asyncFactory = factory
|
||||
node.asyncMeta = { data, context, children, tag }
|
||||
return node
|
||||
}
|
||||
|
||||
export function resolveAsyncComponent (
|
||||
factory: Function,
|
||||
baseCtor: Class<Component>,
|
||||
|
||||
+25
-4
@@ -5,9 +5,11 @@ import { cached, isUndef } from 'shared/util'
|
||||
|
||||
const normalizeEvent = cached((name: string): {
|
||||
name: string,
|
||||
plain: boolean,
|
||||
once: boolean,
|
||||
capture: boolean,
|
||||
passive: boolean
|
||||
passive: boolean,
|
||||
handler?: Function
|
||||
} => {
|
||||
const passive = name.charAt(0) === '&'
|
||||
name = passive ? name.slice(1) : name
|
||||
@@ -15,8 +17,10 @@ const normalizeEvent = cached((name: string): {
|
||||
name = once ? name.slice(1) : name
|
||||
const capture = name.charAt(0) === '!'
|
||||
name = capture ? name.slice(1) : name
|
||||
const plain = !(passive || once || capture)
|
||||
return {
|
||||
name,
|
||||
plain,
|
||||
once,
|
||||
capture,
|
||||
passive
|
||||
@@ -27,8 +31,9 @@ export function createFnInvoker (fns: Function | Array<Function>): Function {
|
||||
function invoker () {
|
||||
const fns = invoker.fns
|
||||
if (Array.isArray(fns)) {
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
fns[i].apply(null, arguments)
|
||||
const cloned = fns.slice()
|
||||
for (let i = 0; i < cloned.length; i++) {
|
||||
cloned[i].apply(null, arguments)
|
||||
}
|
||||
} else {
|
||||
// return handler return value for single handlers
|
||||
@@ -39,6 +44,11 @@ export function createFnInvoker (fns: Function | Array<Function>): Function {
|
||||
return invoker
|
||||
}
|
||||
|
||||
// #6552
|
||||
function prioritizePlainEvents (a, b) {
|
||||
return a.plain ? -1 : b.plain ? 1 : 0
|
||||
}
|
||||
|
||||
export function updateListeners (
|
||||
on: Object,
|
||||
oldOn: Object,
|
||||
@@ -47,10 +57,13 @@ export function updateListeners (
|
||||
vm: Component
|
||||
) {
|
||||
let name, cur, old, event
|
||||
const toAdd = []
|
||||
let hasModifier = false
|
||||
for (name in on) {
|
||||
cur = on[name]
|
||||
old = oldOn[name]
|
||||
event = normalizeEvent(name)
|
||||
if (!event.plain) hasModifier = true
|
||||
if (isUndef(cur)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`Invalid handler for event "${event.name}": got ` + String(cur),
|
||||
@@ -60,12 +73,20 @@ export function updateListeners (
|
||||
if (isUndef(cur.fns)) {
|
||||
cur = on[name] = createFnInvoker(cur)
|
||||
}
|
||||
add(event.name, cur, event.once, event.capture, event.passive)
|
||||
event.handler = cur
|
||||
toAdd.push(event)
|
||||
} else if (cur !== old) {
|
||||
old.fns = cur
|
||||
on[name] = old
|
||||
}
|
||||
}
|
||||
if (toAdd.length) {
|
||||
if (hasModifier) toAdd.sort(prioritizePlainEvents)
|
||||
for (let i = 0; i < toAdd.length; i++) {
|
||||
const event = toAdd[i]
|
||||
add(event.name, event.handler, event.once, event.capture, event.passive)
|
||||
}
|
||||
}
|
||||
for (name in oldOn) {
|
||||
if (isUndef(on[name])) {
|
||||
event = normalizeEvent(name)
|
||||
|
||||
+4
-3
@@ -32,10 +32,11 @@ export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) {
|
||||
}
|
||||
} else {
|
||||
if (vnode.data.refInFor) {
|
||||
if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) {
|
||||
refs[key].push(ref)
|
||||
} else {
|
||||
if (!Array.isArray(refs[key])) {
|
||||
refs[key] = [ref]
|
||||
} else if (refs[key].indexOf(ref) < 0) {
|
||||
// $flow-disable-line
|
||||
refs[key].push(ref)
|
||||
}
|
||||
} else {
|
||||
refs[key] = ref
|
||||
|
||||
+103
-42
@@ -6,8 +6,6 @@
|
||||
*
|
||||
* modified by Evan You (@yyx990803)
|
||||
*
|
||||
|
||||
/*
|
||||
* Not type-checking this because this file is perf-critical and the cost
|
||||
* of making flow understand it is not worth it.
|
||||
*/
|
||||
@@ -17,6 +15,7 @@ import config from '../config'
|
||||
import { SSR_ATTR } from 'shared/constants'
|
||||
import { registerRef } from './modules/ref'
|
||||
import { activeInstance } from '../instance/lifecycle'
|
||||
import { isTextInputType } from 'web/util/element'
|
||||
|
||||
import {
|
||||
warn,
|
||||
@@ -33,22 +32,27 @@ const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
|
||||
|
||||
function sameVnode (a, b) {
|
||||
return (
|
||||
a.key === b.key &&
|
||||
a.tag === b.tag &&
|
||||
a.isComment === b.isComment &&
|
||||
isDef(a.data) === isDef(b.data) &&
|
||||
sameInputType(a, b)
|
||||
a.key === b.key && (
|
||||
(
|
||||
a.tag === b.tag &&
|
||||
a.isComment === b.isComment &&
|
||||
isDef(a.data) === isDef(b.data) &&
|
||||
sameInputType(a, b)
|
||||
) || (
|
||||
isTrue(a.isAsyncPlaceholder) &&
|
||||
a.asyncFactory === b.asyncFactory &&
|
||||
isUndef(b.asyncFactory.error)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Some browsers do not support dynamically changing type for <input>
|
||||
// so they need to be treated as different nodes
|
||||
function sameInputType (a, b) {
|
||||
if (a.tag !== 'input') return true
|
||||
let i
|
||||
const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
|
||||
const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
|
||||
return typeA === typeB
|
||||
return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
|
||||
}
|
||||
|
||||
function createKeyToOldIdx (children, beginIdx, endIdx) {
|
||||
@@ -397,10 +401,11 @@ export function createPatchFunction (backend) {
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
} else {
|
||||
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
|
||||
idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
|
||||
idxInOld = isDef(newStartVnode.key)
|
||||
? oldKeyToIdx[newStartVnode.key]
|
||||
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
|
||||
if (isUndef(idxInOld)) { // New element
|
||||
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
} else {
|
||||
elmToMove = oldCh[idxInOld]
|
||||
/* istanbul ignore if */
|
||||
@@ -413,14 +418,13 @@ export function createPatchFunction (backend) {
|
||||
if (sameVnode(elmToMove, newStartVnode)) {
|
||||
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue)
|
||||
oldCh[idxInOld] = undefined
|
||||
canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
canMove && nodeOps.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm)
|
||||
} else {
|
||||
// same key but different element. treat as new element
|
||||
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
}
|
||||
}
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
}
|
||||
}
|
||||
if (oldStartIdx > oldEndIdx) {
|
||||
@@ -431,10 +435,29 @@ export function createPatchFunction (backend) {
|
||||
}
|
||||
}
|
||||
|
||||
function findIdxInOld (node, oldCh, start, end) {
|
||||
for (let i = start; i < end; i++) {
|
||||
const c = oldCh[i]
|
||||
if (isDef(c) && sameVnode(node, c)) return i
|
||||
}
|
||||
}
|
||||
|
||||
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
|
||||
if (oldVnode === vnode) {
|
||||
return
|
||||
}
|
||||
|
||||
const elm = vnode.elm = oldVnode.elm
|
||||
|
||||
if (isTrue(oldVnode.isAsyncPlaceholder)) {
|
||||
if (isDef(vnode.asyncFactory.resolved)) {
|
||||
hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
|
||||
} else {
|
||||
vnode.isAsyncPlaceholder = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// reuse element for static trees.
|
||||
// note we only do this if the vnode is cloned -
|
||||
// if the new node is not cloned it means the render functions have been
|
||||
@@ -444,16 +467,16 @@ export function createPatchFunction (backend) {
|
||||
vnode.key === oldVnode.key &&
|
||||
(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
|
||||
) {
|
||||
vnode.elm = oldVnode.elm
|
||||
vnode.componentInstance = oldVnode.componentInstance
|
||||
return
|
||||
}
|
||||
|
||||
let i
|
||||
const data = vnode.data
|
||||
if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
|
||||
i(oldVnode, vnode)
|
||||
}
|
||||
const elm = vnode.elm = oldVnode.elm
|
||||
|
||||
const oldCh = oldVnode.children
|
||||
const ch = vnode.children
|
||||
if (isDef(data) && isPatchable(vnode)) {
|
||||
@@ -498,6 +521,11 @@ export function createPatchFunction (backend) {
|
||||
|
||||
// Note: this is a browser-only function so we can assume elms are DOM nodes.
|
||||
function hydrate (elm, vnode, insertedVnodeQueue) {
|
||||
if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
|
||||
vnode.elm = elm
|
||||
vnode.isAsyncPlaceholder = true
|
||||
return true
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!assertNodeMatch(elm, vnode)) {
|
||||
return false
|
||||
@@ -519,27 +547,46 @@ export function createPatchFunction (backend) {
|
||||
if (!elm.hasChildNodes()) {
|
||||
createChildren(vnode, children, insertedVnodeQueue)
|
||||
} else {
|
||||
let childrenMatch = true
|
||||
let childNode = elm.firstChild
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue)) {
|
||||
childrenMatch = false
|
||||
break
|
||||
// v-html and domProps: innerHTML
|
||||
if (isDef(i = data) && isDef(i = i.domProps) && isDef(i = i.innerHTML)) {
|
||||
if (i !== elm.innerHTML) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('server innerHTML: ', i)
|
||||
console.warn('client innerHTML: ', elm.innerHTML)
|
||||
}
|
||||
return false
|
||||
}
|
||||
childNode = childNode.nextSibling
|
||||
}
|
||||
// if childNode is not null, it means the actual childNodes list is
|
||||
// longer than the virtual children list.
|
||||
if (!childrenMatch || childNode) {
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children)
|
||||
} else {
|
||||
// iterate and compare children lists
|
||||
let childrenMatch = true
|
||||
let childNode = elm.firstChild
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue)) {
|
||||
childrenMatch = false
|
||||
break
|
||||
}
|
||||
childNode = childNode.nextSibling
|
||||
}
|
||||
// if childNode is not null, it means the actual childNodes list is
|
||||
// longer than the virtual children list.
|
||||
if (!childrenMatch || childNode) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,14 +677,28 @@ export function createPatchFunction (backend) {
|
||||
// component root element replaced.
|
||||
// update parent placeholder node element, recursively
|
||||
let ancestor = vnode.parent
|
||||
const patchable = isPatchable(vnode)
|
||||
while (ancestor) {
|
||||
ancestor.elm = vnode.elm
|
||||
ancestor = ancestor.parent
|
||||
}
|
||||
if (isPatchable(vnode)) {
|
||||
for (let i = 0; i < cbs.create.length; ++i) {
|
||||
cbs.create[i](emptyNode, vnode.parent)
|
||||
for (let i = 0; i < cbs.destroy.length; ++i) {
|
||||
cbs.destroy[i](ancestor)
|
||||
}
|
||||
ancestor.elm = vnode.elm
|
||||
if (patchable) {
|
||||
for (let i = 0; i < cbs.create.length; ++i) {
|
||||
cbs.create[i](emptyNode, ancestor)
|
||||
}
|
||||
// #6513
|
||||
// invoke insert hooks that may have been merged by create hooks.
|
||||
// e.g. for directives that uses the "inserted" hook.
|
||||
const insert = ancestor.data.hook.insert
|
||||
if (insert.merged) {
|
||||
// start at index 1 to avoid re-invoking component mounted hook
|
||||
for (let i = 1; i < insert.fns.length; i++) {
|
||||
insert.fns[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
ancestor = ancestor.parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-7
@@ -19,6 +19,10 @@ export default class VNode {
|
||||
isComment: boolean; // empty comment placeholder?
|
||||
isCloned: boolean; // is a cloned node?
|
||||
isOnce: boolean; // is a v-once node?
|
||||
asyncFactory: Function | void; // async component factory function
|
||||
asyncMeta: Object | void;
|
||||
isAsyncPlaceholder: boolean;
|
||||
ssrContext: Object | void;
|
||||
|
||||
constructor (
|
||||
tag?: string,
|
||||
@@ -27,7 +31,8 @@ export default class VNode {
|
||||
text?: string,
|
||||
elm?: Node,
|
||||
context?: Component,
|
||||
componentOptions?: VNodeComponentOptions
|
||||
componentOptions?: VNodeComponentOptions,
|
||||
asyncFactory?: Function
|
||||
) {
|
||||
this.tag = tag
|
||||
this.data = data
|
||||
@@ -47,6 +52,9 @@ export default class VNode {
|
||||
this.isComment = false
|
||||
this.isCloned = false
|
||||
this.isOnce = false
|
||||
this.asyncFactory = asyncFactory
|
||||
this.asyncMeta = undefined
|
||||
this.isAsyncPlaceholder = false
|
||||
}
|
||||
|
||||
// DEPRECATED: alias for componentInstance for backwards compat.
|
||||
@@ -56,9 +64,9 @@ export default class VNode {
|
||||
}
|
||||
}
|
||||
|
||||
export const createEmptyVNode = () => {
|
||||
export const createEmptyVNode = (text: string = '') => {
|
||||
const node = new VNode()
|
||||
node.text = ''
|
||||
node.text = text
|
||||
node.isComment = true
|
||||
return node
|
||||
}
|
||||
@@ -71,7 +79,7 @@ export function createTextVNode (val: string | number) {
|
||||
// used for static nodes and slot nodes because they may be reused across
|
||||
// multiple renders, cloning them avoids errors when DOM manipulations rely
|
||||
// on their elm reference.
|
||||
export function cloneVNode (vnode: VNode): VNode {
|
||||
export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
|
||||
const cloned = new VNode(
|
||||
vnode.tag,
|
||||
vnode.data,
|
||||
@@ -79,21 +87,25 @@ export function cloneVNode (vnode: VNode): VNode {
|
||||
vnode.text,
|
||||
vnode.elm,
|
||||
vnode.context,
|
||||
vnode.componentOptions
|
||||
vnode.componentOptions,
|
||||
vnode.asyncFactory
|
||||
)
|
||||
cloned.ns = vnode.ns
|
||||
cloned.isStatic = vnode.isStatic
|
||||
cloned.key = vnode.key
|
||||
cloned.isComment = vnode.isComment
|
||||
cloned.isCloned = true
|
||||
if (deep && vnode.children) {
|
||||
cloned.children = cloneVNodes(vnode.children)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
export function cloneVNodes (vnodes: Array<VNode>): Array<VNode> {
|
||||
export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> {
|
||||
const len = vnodes.length
|
||||
const res = new Array(len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
res[i] = cloneVNode(vnodes[i])
|
||||
res[i] = cloneVNode(vnodes[i], deep)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
export { parseComponent } from 'sfc/parser'
|
||||
export { compile, compileToFunctions } from './compiler/index'
|
||||
+7
-3
@@ -40,7 +40,11 @@ export default function model (
|
||||
}
|
||||
}
|
||||
|
||||
if (tag === 'select') {
|
||||
if (el.component) {
|
||||
genComponentModel(el, value, modifiers)
|
||||
// component v-model doesn't need extra runtime
|
||||
return false
|
||||
} else if (tag === 'select') {
|
||||
genSelect(el, value, modifiers)
|
||||
} else if (tag === 'input' && type === 'checkbox') {
|
||||
genCheckboxModel(el, value, modifiers)
|
||||
@@ -89,7 +93,7 @@ function genCheckboxModel (
|
||||
'if(Array.isArray($$a)){' +
|
||||
`var $$v=${number ? '_n(' + valueBinding + ')' : valueBinding},` +
|
||||
'$$i=_i($$a,$$v);' +
|
||||
`if($$c){$$i<0&&(${value}=$$a.concat($$v))}` +
|
||||
`if($$el.checked){$$i<0&&(${value}=$$a.concat([$$v]))}` +
|
||||
`else{$$i>-1&&(${value}=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}` +
|
||||
`}else{${genAssignmentCode(value, '$$c')}}`,
|
||||
null, true
|
||||
@@ -154,7 +158,7 @@ function genDefaultModel (
|
||||
|
||||
addProp(el, 'value', `(${value})`)
|
||||
addHandler(el, event, code, null, true)
|
||||
if (trim || number || type === 'number') {
|
||||
if (trim || number) {
|
||||
addHandler(el, 'blur', '$forceUpdate()')
|
||||
}
|
||||
}
|
||||
|
||||
+2
-25
@@ -1,31 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import { isUnaryTag, canBeLeftOpenTag } from './util'
|
||||
import { genStaticKeys } from 'shared/util'
|
||||
import { baseOptions } from './options'
|
||||
import { createCompiler } from 'compiler/index'
|
||||
|
||||
import modules from './modules/index'
|
||||
import directives from './directives/index'
|
||||
|
||||
import {
|
||||
isPreTag,
|
||||
mustUseProp,
|
||||
isReservedTag,
|
||||
getTagNamespace
|
||||
} from '../util/index'
|
||||
|
||||
export const baseOptions: CompilerOptions = {
|
||||
expectHTML: true,
|
||||
modules,
|
||||
directives,
|
||||
isPreTag,
|
||||
isUnaryTag,
|
||||
mustUseProp,
|
||||
canBeLeftOpenTag,
|
||||
isReservedTag,
|
||||
getTagNamespace,
|
||||
staticKeys: genStaticKeys(modules)
|
||||
}
|
||||
|
||||
const { compile, compileToFunctions } = createCompiler(baseOptions)
|
||||
|
||||
export { compile, compileToFunctions }
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
import config from 'core/config'
|
||||
import { warn, cached } from 'core/util/index'
|
||||
import { mark, measure } from 'core/util/perf'
|
||||
|
||||
import Vue from './runtime/index'
|
||||
import { query } from './util/index'
|
||||
import { shouldDecodeNewlines } from './util/compat'
|
||||
import { compileToFunctions } from './compiler/index'
|
||||
|
||||
const idToTemplate = cached(id => {
|
||||
const el = query(id)
|
||||
return el && el.innerHTML
|
||||
})
|
||||
|
||||
const mount = Vue.prototype.$mount
|
||||
Vue.prototype.$mount = function (
|
||||
el?: string | Element,
|
||||
hydrating?: boolean
|
||||
): Component {
|
||||
el = el && query(el)
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (el === document.body || el === document.documentElement) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
const options = this.$options
|
||||
// resolve template/el and convert to render function
|
||||
if (!options.render) {
|
||||
let template = options.template
|
||||
if (template) {
|
||||
if (typeof template === 'string') {
|
||||
if (template.charAt(0) === '#') {
|
||||
template = idToTemplate(template)
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' && !template) {
|
||||
warn(
|
||||
`Template element not found or is empty: ${options.template}`,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (template.nodeType) {
|
||||
template = template.innerHTML
|
||||
} else {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warn('invalid template option:' + template, this)
|
||||
}
|
||||
return this
|
||||
}
|
||||
} else if (el) {
|
||||
template = getOuterHTML(el)
|
||||
}
|
||||
if (template) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
|
||||
mark('compile')
|
||||
}
|
||||
|
||||
const { render, staticRenderFns } = compileToFunctions(template, {
|
||||
shouldDecodeNewlines,
|
||||
delimiters: options.delimiters
|
||||
}, this)
|
||||
options.render = render
|
||||
options.staticRenderFns = staticRenderFns
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
|
||||
mark('compile end')
|
||||
measure(`${this._name} compile`, 'compile', 'compile end')
|
||||
}
|
||||
}
|
||||
}
|
||||
return mount.call(this, el, hydrating)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get outerHTML of elements, taking care
|
||||
* of SVG elements in IE as well.
|
||||
*/
|
||||
function getOuterHTML (el: Element): string {
|
||||
if (el.outerHTML) {
|
||||
return el.outerHTML
|
||||
} else {
|
||||
const container = document.createElement('div')
|
||||
container.appendChild(el.cloneNode(true))
|
||||
return container.innerHTML
|
||||
}
|
||||
}
|
||||
|
||||
Vue.compile = compileToFunctions
|
||||
|
||||
export default Vue
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
import Vue from './runtime/index'
|
||||
|
||||
export default Vue
|
||||
+9
-1
@@ -42,12 +42,20 @@ export function removeClass (el: HTMLElement, cls: ?string) {
|
||||
} else {
|
||||
el.classList.remove(cls)
|
||||
}
|
||||
if (!el.classList.length) {
|
||||
el.removeAttribute('class')
|
||||
}
|
||||
} else {
|
||||
let cur = ` ${el.getAttribute('class') || ''} `
|
||||
const tar = ' ' + cls + ' '
|
||||
while (cur.indexOf(tar) >= 0) {
|
||||
cur = cur.replace(tar, ' ')
|
||||
}
|
||||
el.setAttribute('class', cur.trim())
|
||||
cur = cur.trim()
|
||||
if (cur) {
|
||||
el.setAttribute('class', cur)
|
||||
} else {
|
||||
el.removeAttribute('class')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -127,7 +127,8 @@ export default {
|
||||
if (!hasTransition) {
|
||||
return false
|
||||
}
|
||||
if (this._hasMove != null) {
|
||||
/* istanbul ignore if */
|
||||
if (this._hasMove) {
|
||||
return this._hasMove
|
||||
}
|
||||
// Detect whether an element with the move class applied has
|
||||
|
||||
+19
-5
@@ -5,7 +5,11 @@
|
||||
|
||||
import { warn } from 'core/util/index'
|
||||
import { camelize, extend, isPrimitive } from 'shared/util'
|
||||
import { mergeVNodeHook, getFirstComponentChild } from 'core/vdom/helpers/index'
|
||||
import {
|
||||
mergeVNodeHook,
|
||||
isAsyncPlaceholder,
|
||||
getFirstComponentChild
|
||||
} from 'core/vdom/helpers/index'
|
||||
|
||||
export const transitionProps = {
|
||||
name: String,
|
||||
@@ -78,13 +82,13 @@ export default {
|
||||
abstract: true,
|
||||
|
||||
render (h: Function) {
|
||||
let children: ?Array<VNode> = this.$slots.default
|
||||
let children: ?Array<VNode> = this.$options._renderChildren
|
||||
if (!children) {
|
||||
return
|
||||
}
|
||||
|
||||
// filter out text nodes (possible whitespaces)
|
||||
children = children.filter((c: VNode) => c.tag)
|
||||
children = children.filter((c: VNode) => c.tag || isAsyncPlaceholder(c))
|
||||
/* istanbul ignore if */
|
||||
if (!children.length) {
|
||||
return
|
||||
@@ -136,7 +140,9 @@ export default {
|
||||
// during entering.
|
||||
const id: string = `__transition-${this._uid}-`
|
||||
child.key = child.key == null
|
||||
? id + child.tag
|
||||
? child.isComment
|
||||
? id + 'comment'
|
||||
: id + child.tag
|
||||
: isPrimitive(child.key)
|
||||
? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key)
|
||||
: child.key
|
||||
@@ -151,7 +157,12 @@ export default {
|
||||
child.data.show = true
|
||||
}
|
||||
|
||||
if (oldChild && oldChild.data && !isSameChild(child, oldChild)) {
|
||||
if (
|
||||
oldChild &&
|
||||
oldChild.data &&
|
||||
!isSameChild(child, oldChild) &&
|
||||
!isAsyncPlaceholder(oldChild)
|
||||
) {
|
||||
// replace old child transition data with fresh one
|
||||
// important for dynamic transitions!
|
||||
const oldData: Object = oldChild && (oldChild.data.transition = extend({}, data))
|
||||
@@ -165,6 +176,9 @@ export default {
|
||||
})
|
||||
return placeholder(h, rawChild)
|
||||
} else if (mode === 'in-out') {
|
||||
if (isAsyncPlaceholder(child)) {
|
||||
return oldRawChild
|
||||
}
|
||||
let delayedLeave
|
||||
const performLeave = () => { delayedLeave() }
|
||||
mergeVNodeHook(data, 'afterEnter', performLeave)
|
||||
|
||||
+26
-20
@@ -3,6 +3,7 @@
|
||||
* properties to Elements.
|
||||
*/
|
||||
|
||||
import { isTextInputType } from 'web/util/element'
|
||||
import { looseEqual, looseIndexOf } from 'shared/util'
|
||||
import { warn, isAndroid, isIE9, isIE, isEdge } from 'core/util/index'
|
||||
|
||||
@@ -20,15 +21,9 @@ if (isIE9) {
|
||||
export default {
|
||||
inserted (el, binding, vnode) {
|
||||
if (vnode.tag === 'select') {
|
||||
const cb = () => {
|
||||
setSelected(el, binding, vnode.context)
|
||||
}
|
||||
cb()
|
||||
/* istanbul ignore if */
|
||||
if (isIE || isEdge) {
|
||||
setTimeout(cb, 0)
|
||||
}
|
||||
} else if (vnode.tag === 'textarea' || el.type === 'text' || el.type === 'password') {
|
||||
setSelected(el, binding, vnode.context)
|
||||
el._vOptions = [].map.call(el.options, getValue)
|
||||
} else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
|
||||
el._vModifiers = binding.modifiers
|
||||
if (!binding.modifiers.lazy) {
|
||||
// Safari < 10.2 & UIWebView doesn't fire compositionend when
|
||||
@@ -54,17 +49,33 @@ export default {
|
||||
// it's possible that the value is out-of-sync with the rendered options.
|
||||
// detect such cases and filter out values that no longer has a matching
|
||||
// option in the DOM.
|
||||
const needReset = el.multiple
|
||||
? binding.value.some(v => hasNoMatchingOption(v, el.options))
|
||||
: binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, el.options)
|
||||
if (needReset) {
|
||||
trigger(el, 'change')
|
||||
const prevOptions = el._vOptions
|
||||
const curOptions = el._vOptions = [].map.call(el.options, getValue)
|
||||
if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
|
||||
// trigger change event if
|
||||
// no matching option found for at least one value
|
||||
const needReset = el.multiple
|
||||
? binding.value.some(v => hasNoMatchingOption(v, curOptions))
|
||||
: binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions)
|
||||
if (needReset) {
|
||||
trigger(el, 'change')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setSelected (el, binding, vm) {
|
||||
actuallySetSelected(el, binding, vm)
|
||||
/* istanbul ignore if */
|
||||
if (isIE || isEdge) {
|
||||
setTimeout(() => {
|
||||
actuallySetSelected(el, binding, vm)
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function actuallySetSelected (el, binding, vm) {
|
||||
const value = binding.value
|
||||
const isMultiple = el.multiple
|
||||
if (isMultiple && !Array.isArray(value)) {
|
||||
@@ -100,12 +111,7 @@ function setSelected (el, binding, vm) {
|
||||
}
|
||||
|
||||
function hasNoMatchingOption (value, options) {
|
||||
for (let i = 0, l = options.length; i < l; i++) {
|
||||
if (looseEqual(getValue(options[i]), value)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return options.every(o => !looseEqual(o, value))
|
||||
}
|
||||
|
||||
function getValue (option) {
|
||||
|
||||
+2
-3
@@ -1,6 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import { isIE9 } from 'core/util/env'
|
||||
import { enter, leave } from '../modules/transition'
|
||||
|
||||
// recursively search for possible transition defined inside the component root
|
||||
@@ -16,7 +15,7 @@ export default {
|
||||
const transition = vnode.data && vnode.data.transition
|
||||
const originalDisplay = el.__vOriginalDisplay =
|
||||
el.style.display === 'none' ? '' : el.style.display
|
||||
if (value && transition && !isIE9) {
|
||||
if (value && transition) {
|
||||
vnode.data.show = true
|
||||
enter(vnode, () => {
|
||||
el.style.display = originalDisplay
|
||||
@@ -31,7 +30,7 @@ export default {
|
||||
if (value === oldValue) return
|
||||
vnode = locateNode(vnode)
|
||||
const transition = vnode.data && vnode.data.transition
|
||||
if (transition && !isIE9) {
|
||||
if (transition) {
|
||||
vnode.data.show = true
|
||||
if (value) {
|
||||
enter(vnode, () => {
|
||||
|
||||
+10
-1
@@ -18,6 +18,10 @@ import {
|
||||
} from 'web/util/index'
|
||||
|
||||
function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
|
||||
const opts = vnode.componentOptions
|
||||
if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
|
||||
return
|
||||
}
|
||||
if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
|
||||
return
|
||||
}
|
||||
@@ -60,7 +64,12 @@ function setAttr (el: Element, key: string, value: any) {
|
||||
if (isFalsyAttrValue(value)) {
|
||||
el.removeAttribute(key)
|
||||
} else {
|
||||
el.setAttribute(key, key)
|
||||
// technically allowfullscreen is a boolean attribute for <iframe>,
|
||||
// but Flash expects a value of "true" when used on <embed> tag
|
||||
value = key === 'allowfullscreen' && el.tagName === 'EMBED'
|
||||
? 'true'
|
||||
: key
|
||||
el.setAttribute(key, value)
|
||||
}
|
||||
} else if (isEnumeratedAttr(key)) {
|
||||
el.setAttribute(key, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true')
|
||||
|
||||
+8
-3
@@ -61,14 +61,19 @@ function shouldUpdateValue (
|
||||
}
|
||||
|
||||
function isDirty (elm: acceptValueElm, checkVal: string): boolean {
|
||||
// return true when textbox (.number and .trim) loses focus and its value is not equal to the updated value
|
||||
return document.activeElement !== elm && elm.value !== checkVal
|
||||
// return true when textbox (.number and .trim) loses focus and its value is
|
||||
// not equal to the updated value
|
||||
let notInFocus = true
|
||||
// #6157
|
||||
// work around IE bug when accessing document.activeElement in an iframe
|
||||
try { notInFocus = document.activeElement !== elm } catch (e) {}
|
||||
return notInFocus && elm.value !== checkVal
|
||||
}
|
||||
|
||||
function isInputChanged (elm: any, newVal: string): boolean {
|
||||
const value = elm.value
|
||||
const modifiers = elm._vModifiers // injected by v-model runtime
|
||||
if ((isDef(modifiers) && modifiers.number) || elm.type === 'number') {
|
||||
if (isDef(modifiers) && modifiers.number) {
|
||||
return toNumber(value) !== toNumber(newVal)
|
||||
}
|
||||
if (isDef(modifiers) && modifiers.trim) {
|
||||
|
||||
+10
-10
@@ -26,20 +26,20 @@ const setProp = (el, name, val) => {
|
||||
}
|
||||
}
|
||||
|
||||
const prefixes = ['Webkit', 'Moz', 'ms']
|
||||
const vendorNames = ['Webkit', 'Moz', 'ms']
|
||||
|
||||
let testEl
|
||||
let emptyStyle
|
||||
const normalize = cached(function (prop) {
|
||||
testEl = testEl || document.createElement('div')
|
||||
emptyStyle = emptyStyle || document.createElement('div').style
|
||||
prop = camelize(prop)
|
||||
if (prop !== 'filter' && (prop in testEl.style)) {
|
||||
if (prop !== 'filter' && (prop in emptyStyle)) {
|
||||
return prop
|
||||
}
|
||||
const upper = prop.charAt(0).toUpperCase() + prop.slice(1)
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
const prefixed = prefixes[i] + upper
|
||||
if (prefixed in testEl.style) {
|
||||
return prefixed
|
||||
const capName = prop.charAt(0).toUpperCase() + prop.slice(1)
|
||||
for (let i = 0; i < vendorNames.length; i++) {
|
||||
const name = vendorNames[i] + capName
|
||||
if (name in emptyStyle) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -65,7 +65,7 @@ function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) {
|
||||
const style = normalizeStyleBinding(vnode.data.style) || {}
|
||||
|
||||
// store normalized style under a different key for next diff
|
||||
// make sure to clone it if it's reactive, since the user likley wants
|
||||
// make sure to clone it if it's reactive, since the user likely wants
|
||||
// to mutate it.
|
||||
vnode.data.normalizedStyle = isDef(style.__ob__)
|
||||
? extend({}, style)
|
||||
|
||||
+5
-2
@@ -69,8 +69,11 @@ export function nextFrame (fn: Function) {
|
||||
}
|
||||
|
||||
export function addTransitionClass (el: any, cls: string) {
|
||||
(el._transitionClasses || (el._transitionClasses = [])).push(cls)
|
||||
addClass(el, cls)
|
||||
const transitionClasses = el._transitionClasses || (el._transitionClasses = [])
|
||||
if (transitionClasses.indexOf(cls) < 0) {
|
||||
transitionClasses.push(cls)
|
||||
addClass(el, cls)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeTransitionClass (el: any, cls: string) {
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
process.env.VUE_ENV = 'server'
|
||||
|
||||
import modules from './server/modules/index'
|
||||
import baseDirectives from './server/directives/index'
|
||||
import { isUnaryTag, canBeLeftOpenTag } from './compiler/util'
|
||||
|
||||
import { createRenderer as _createRenderer } from 'server/create-renderer'
|
||||
import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer'
|
||||
|
||||
export function createRenderer (options?: Object = {}): {
|
||||
renderToString: Function,
|
||||
renderToStream: Function
|
||||
} {
|
||||
return _createRenderer(Object.assign({}, options, {
|
||||
isUnaryTag,
|
||||
canBeLeftOpenTag,
|
||||
modules,
|
||||
// user can provide server-side implementations for custom directives
|
||||
// when creating the renderer.
|
||||
directives: Object.assign(baseDirectives, options.directives)
|
||||
}))
|
||||
}
|
||||
|
||||
export const createBundleRenderer = createBundleRendererCreator(createRenderer)
|
||||
+10
-7
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { escape } from 'he'
|
||||
import { escape } from '../util'
|
||||
|
||||
import {
|
||||
isDef,
|
||||
@@ -17,12 +17,15 @@ export default function renderAttrs (node: VNodeWithData): string {
|
||||
let attrs = node.data.attrs
|
||||
let res = ''
|
||||
|
||||
let parent = node.parent
|
||||
while (isDef(parent)) {
|
||||
if (isDef(parent.data) && isDef(parent.data.attrs)) {
|
||||
attrs = Object.assign({}, attrs, parent.data.attrs)
|
||||
const opts = node.parent && node.parent.componentOptions
|
||||
if (isUndef(opts) || opts.Ctor.options.inheritAttrs !== false) {
|
||||
let parent = node.parent
|
||||
while (isDef(parent)) {
|
||||
if (isDef(parent.data) && isDef(parent.data.attrs)) {
|
||||
attrs = Object.assign({}, attrs, parent.data.attrs)
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
|
||||
if (isUndef(attrs)) {
|
||||
@@ -47,7 +50,7 @@ export function renderAttr (key: string, value: string): string {
|
||||
} else if (isEnumeratedAttr(key)) {
|
||||
return ` ${key}="${isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'}"`
|
||||
} else if (!isFalsyAttrValue(value)) {
|
||||
return ` ${key}="${typeof value === 'string' ? escape(value) : value}"`
|
||||
return ` ${key}="${escape(String(value))}"`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { escape } from 'he'
|
||||
import { escape } from '../util'
|
||||
import { genClassForVnode } from 'web/util/index'
|
||||
|
||||
export default function renderClass (node: VNodeWithData): ?string {
|
||||
|
||||
+3
-4
@@ -1,12 +1,11 @@
|
||||
/* @flow */
|
||||
|
||||
import { escape } from 'he'
|
||||
import { escape } from '../util'
|
||||
import { hyphenate } from 'shared/util'
|
||||
import { getStyle } from 'web/util/style'
|
||||
|
||||
function genStyleText (vnode: VNode): string {
|
||||
export function genStyle (style: Object): string {
|
||||
let styleText = ''
|
||||
const style = getStyle(vnode, false)
|
||||
for (const key in style) {
|
||||
const value = style[key]
|
||||
const hyphenatedKey = hyphenate(key)
|
||||
@@ -22,7 +21,7 @@ function genStyleText (vnode: VNode): string {
|
||||
}
|
||||
|
||||
export default function renderStyle (vnode: VNodeWithData): ?string {
|
||||
const styleText = genStyleText(vnode)
|
||||
const styleText = genStyle(getStyle(vnode, false))
|
||||
if (styleText !== '') {
|
||||
return ` style=${JSON.stringify(escape(styleText))}`
|
||||
}
|
||||
|
||||
+15
@@ -34,3 +34,18 @@ export const propsToAttrMap = {
|
||||
htmlFor: 'for',
|
||||
httpEquiv: 'http-equiv'
|
||||
}
|
||||
|
||||
const ESC = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'&': '&'
|
||||
}
|
||||
|
||||
export function escape (s: string) {
|
||||
return s.replace(/[<>"&]/g, escapeChar)
|
||||
}
|
||||
|
||||
function escapeChar (a) {
|
||||
return ESC[a] || a
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { makeMap } from 'shared/util'
|
||||
export const isReservedAttr = makeMap('style,class')
|
||||
|
||||
// attributes that should be using props for binding
|
||||
const acceptValue = makeMap('input,textarea,option,select')
|
||||
const acceptValue = makeMap('input,textarea,option,select,progress')
|
||||
export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => {
|
||||
return (
|
||||
(attr === 'value' && acceptValue(tag)) && type !== 'button' ||
|
||||
|
||||
+34
-25
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
|
||||
import { isDef, isUndef, isObject } from 'shared/util'
|
||||
import { isDef, isObject } from 'shared/util'
|
||||
|
||||
export function genClassForVnode (vnode: VNode): string {
|
||||
let data = vnode.data
|
||||
@@ -17,7 +17,7 @@ export function genClassForVnode (vnode: VNode): string {
|
||||
data = mergeClassData(data, parentNode.data)
|
||||
}
|
||||
}
|
||||
return genClassFromData(data)
|
||||
return renderClass(data.staticClass, data.class)
|
||||
}
|
||||
|
||||
function mergeClassData (child: VNodeData, parent: VNodeData): {
|
||||
@@ -32,9 +32,10 @@ function mergeClassData (child: VNodeData, parent: VNodeData): {
|
||||
}
|
||||
}
|
||||
|
||||
function genClassFromData (data: Object): string {
|
||||
const dynamicClass = data.class
|
||||
const staticClass = data.staticClass
|
||||
export function renderClass (
|
||||
staticClass: ?string,
|
||||
dynamicClass: any
|
||||
): string {
|
||||
if (isDef(staticClass) || isDef(dynamicClass)) {
|
||||
return concat(staticClass, stringifyClass(dynamicClass))
|
||||
}
|
||||
@@ -47,30 +48,38 @@ export function concat (a: ?string, b: ?string): string {
|
||||
}
|
||||
|
||||
export function stringifyClass (value: any): string {
|
||||
if (isUndef(value)) {
|
||||
return ''
|
||||
if (Array.isArray(value)) {
|
||||
return stringifyArray(value)
|
||||
}
|
||||
if (isObject(value)) {
|
||||
return stringifyObject(value)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
}
|
||||
let res = ''
|
||||
if (Array.isArray(value)) {
|
||||
let stringified
|
||||
for (let i = 0, l = value.length; i < l; i++) {
|
||||
if (isDef(value[i])) {
|
||||
if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
|
||||
res += stringified + ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.slice(0, -1)
|
||||
}
|
||||
if (isObject(value)) {
|
||||
for (const key in value) {
|
||||
if (value[key]) res += key + ' '
|
||||
}
|
||||
return res.slice(0, -1)
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return ''
|
||||
}
|
||||
|
||||
function stringifyArray (value: Array<any>): string {
|
||||
let res = ''
|
||||
let stringified
|
||||
for (let i = 0, l = value.length; i < l; i++) {
|
||||
if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
|
||||
if (res) res += ' '
|
||||
res += stringified
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function stringifyObject (value: Object): string {
|
||||
let res = ''
|
||||
for (const key in value) {
|
||||
if (value[key]) {
|
||||
if (res) res += ' '
|
||||
res += key
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { inBrowser } from 'core/util/index'
|
||||
// check whether current browser encodes a char inside attribute values
|
||||
function shouldDecode (content: string, encoded: string): boolean {
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = `<div a="${content}">`
|
||||
div.innerHTML = `<div a="${content}"/>`
|
||||
return div.innerHTML.indexOf(encoded) > 0
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -11,7 +11,7 @@ export const namespaceMap = {
|
||||
export const isHTMLTag = makeMap(
|
||||
'html,body,base,head,link,meta,style,title,' +
|
||||
'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
|
||||
'div,dd,dl,dt,figcaption,figure,hr,img,li,main,ol,p,pre,ul,' +
|
||||
'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
|
||||
'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
|
||||
's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
|
||||
'embed,object,param,source,canvas,script,noscript,del,ins,' +
|
||||
@@ -19,7 +19,7 @@ export const isHTMLTag = makeMap(
|
||||
'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
|
||||
'output,progress,select,textarea,' +
|
||||
'details,dialog,menu,menuitem,summary,' +
|
||||
'content,element,shadow,template'
|
||||
'content,element,shadow,template,blockquote,iframe,tfoot'
|
||||
)
|
||||
|
||||
// this map is intentionally selective, only covering SVG elements that may
|
||||
@@ -73,3 +73,5 @@ export function isUnknownElement (tag: string): boolean {
|
||||
return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
export const isTextInputType = makeMap('text,number,password,search,email,tel,url')
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export { compile } from 'weex/compiler/index'
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { cached, camelize } from 'shared/util'
|
||||
|
||||
const normalize = cached(camelize)
|
||||
|
||||
function normalizeKeyName (str: string) : string {
|
||||
function normalizeKeyName (str: string): string {
|
||||
if (str.match(/^v\-/)) {
|
||||
return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, ($, directive, prop) => {
|
||||
return directive + normalize(prop)
|
||||
|
||||
-417
@@ -1,417 +0,0 @@
|
||||
import TextNode from 'weex/runtime/text-node'
|
||||
|
||||
// this will be preserved during build
|
||||
const VueFactory = require('./factory')
|
||||
|
||||
const instances = {}
|
||||
const modules = {}
|
||||
const components = {}
|
||||
|
||||
const renderer = {
|
||||
TextNode,
|
||||
instances,
|
||||
modules,
|
||||
components
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare framework config, basically about the virtual-DOM and JS bridge.
|
||||
* @param {object} cfg
|
||||
*/
|
||||
export function init (cfg) {
|
||||
renderer.Document = cfg.Document
|
||||
renderer.Element = cfg.Element
|
||||
renderer.Comment = cfg.Comment
|
||||
renderer.sendTasks = cfg.sendTasks
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset framework config and clear all registrations.
|
||||
*/
|
||||
export function reset () {
|
||||
clear(instances)
|
||||
clear(modules)
|
||||
clear(components)
|
||||
delete renderer.Document
|
||||
delete renderer.Element
|
||||
delete renderer.Comment
|
||||
delete renderer.sendTasks
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all keys of an object.
|
||||
* @param {object} obj
|
||||
*/
|
||||
function clear (obj) {
|
||||
for (const key in obj) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance with id, code, config and external data.
|
||||
* @param {string} instanceId
|
||||
* @param {string} appCode
|
||||
* @param {object} config
|
||||
* @param {object} data
|
||||
* @param {object} env { info, config, services }
|
||||
*/
|
||||
export function createInstance (
|
||||
instanceId,
|
||||
appCode = '',
|
||||
config = {},
|
||||
data,
|
||||
env = {}
|
||||
) {
|
||||
// Virtual-DOM object.
|
||||
const document = new renderer.Document(instanceId, config.bundleUrl)
|
||||
|
||||
// All function/callback of parameters before sent to native
|
||||
// will be converted as an id. So `callbacks` is used to store
|
||||
// these real functions. When a callback invoked and won't be
|
||||
// called again, it should be removed from here automatically.
|
||||
const callbacks = []
|
||||
|
||||
// The latest callback id, incremental.
|
||||
const callbackId = 1
|
||||
|
||||
const instance = instances[instanceId] = {
|
||||
instanceId, config, data,
|
||||
document, callbacks, callbackId
|
||||
}
|
||||
|
||||
// Prepare native module getter and HTML5 Timer APIs.
|
||||
const moduleGetter = genModuleGetter(instanceId)
|
||||
const timerAPIs = getInstanceTimer(instanceId, moduleGetter)
|
||||
|
||||
// Prepare `weex` instance variable.
|
||||
const weexInstanceVar = {
|
||||
config,
|
||||
document,
|
||||
requireModule: moduleGetter
|
||||
}
|
||||
Object.freeze(weexInstanceVar)
|
||||
|
||||
// Each instance has a independent `Vue` module instance
|
||||
const Vue = instance.Vue = createVueModuleInstance(instanceId, moduleGetter)
|
||||
|
||||
// The function which create a closure the JS Bundle will run in.
|
||||
// It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
|
||||
const instanceVars = Object.assign({
|
||||
Vue,
|
||||
weex: weexInstanceVar,
|
||||
// deprecated
|
||||
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
|
||||
}, timerAPIs)
|
||||
callFunction(instanceVars, appCode)
|
||||
|
||||
// Send `createFinish` signal to native.
|
||||
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an instance with id. It will make sure all memory of
|
||||
* this instance released and no more leaks.
|
||||
* @param {string} instanceId
|
||||
*/
|
||||
export function destroyInstance (instanceId) {
|
||||
const instance = instances[instanceId]
|
||||
if (instance && instance.app instanceof instance.Vue) {
|
||||
instance.app.$destroy()
|
||||
}
|
||||
delete instances[instanceId]
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh an instance with id and new top-level component data.
|
||||
* It will use `Vue.set` on all keys of the new data. So it's better
|
||||
* define all possible meaningful keys when instance created.
|
||||
* @param {string} instanceId
|
||||
* @param {object} data
|
||||
*/
|
||||
export function refreshInstance (instanceId, data) {
|
||||
const instance = instances[instanceId]
|
||||
if (!instance || !(instance.app instanceof instance.Vue)) {
|
||||
return new Error(`refreshInstance: instance ${instanceId} not found!`)
|
||||
}
|
||||
for (const key in data) {
|
||||
instance.Vue.set(instance.app, key, data[key])
|
||||
}
|
||||
// Finally `refreshFinish` signal needed.
|
||||
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON object of the root element.
|
||||
* @param {string} instanceId
|
||||
*/
|
||||
export function getRoot (instanceId) {
|
||||
const instance = instances[instanceId]
|
||||
if (!instance || !(instance.app instanceof instance.Vue)) {
|
||||
return new Error(`getRoot: instance ${instanceId} not found!`)
|
||||
}
|
||||
return instance.app.$el.toJSON()
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive tasks from native. Generally there are two types of tasks:
|
||||
* 1. `fireEvent`: an device actions or user actions from native.
|
||||
* 2. `callback`: invoke function which sent to native as a parameter before.
|
||||
* @param {string} instanceId
|
||||
* @param {array} tasks
|
||||
*/
|
||||
export function receiveTasks (instanceId, tasks) {
|
||||
const instance = instances[instanceId]
|
||||
if (!instance || !(instance.app instanceof instance.Vue)) {
|
||||
return new Error(`receiveTasks: instance ${instanceId} not found!`)
|
||||
}
|
||||
const { callbacks, document } = instance
|
||||
tasks.forEach(task => {
|
||||
// `fireEvent` case: find the event target and fire.
|
||||
if (task.method === 'fireEvent') {
|
||||
const [nodeId, type, e, domChanges] = task.args
|
||||
const el = document.getRef(nodeId)
|
||||
document.fireEvent(el, type, e, domChanges)
|
||||
}
|
||||
// `callback` case: find the callback by id and call it.
|
||||
if (task.method === 'callback') {
|
||||
const [callbackId, data, ifKeepAlive] = task.args
|
||||
const callback = callbacks[callbackId]
|
||||
if (typeof callback === 'function') {
|
||||
callback(data)
|
||||
// Remove the callback from `callbacks` if it won't called again.
|
||||
if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
|
||||
callbacks[callbackId] = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// Finally `updateFinish` signal needed.
|
||||
renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register native modules information.
|
||||
* @param {object} newModules
|
||||
*/
|
||||
export function registerModules (newModules) {
|
||||
for (const name in newModules) {
|
||||
if (!modules[name]) {
|
||||
modules[name] = {}
|
||||
}
|
||||
newModules[name].forEach(method => {
|
||||
if (typeof method === 'string') {
|
||||
modules[name][method] = true
|
||||
} else {
|
||||
modules[name][method.name] = method.args
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register native components information.
|
||||
* @param {array} newComponents
|
||||
*/
|
||||
export function registerComponents (newComponents) {
|
||||
if (Array.isArray(newComponents)) {
|
||||
newComponents.forEach(component => {
|
||||
if (!component) {
|
||||
return
|
||||
}
|
||||
if (typeof component === 'string') {
|
||||
components[component] = true
|
||||
} else if (typeof component === 'object' && typeof component.type === 'string') {
|
||||
components[component.type] = component
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fresh instance of Vue for each Weex instance.
|
||||
*/
|
||||
function createVueModuleInstance (instanceId, moduleGetter) {
|
||||
const exports = {}
|
||||
VueFactory(exports, renderer)
|
||||
const Vue = exports.Vue
|
||||
|
||||
const instance = instances[instanceId]
|
||||
|
||||
// patch reserved tag detection to account for dynamically registered
|
||||
// components
|
||||
const isReservedTag = Vue.config.isReservedTag || (() => false)
|
||||
Vue.config.isReservedTag = name => {
|
||||
return components[name] || isReservedTag(name)
|
||||
}
|
||||
|
||||
// expose weex-specific info
|
||||
Vue.prototype.$instanceId = instanceId
|
||||
Vue.prototype.$document = instance.document
|
||||
|
||||
// expose weex native module getter on subVue prototype so that
|
||||
// vdom runtime modules can access native modules via vnode.context
|
||||
Vue.prototype.$requireWeexModule = moduleGetter
|
||||
|
||||
// Hack `Vue` behavior to handle instance information and data
|
||||
// before root component created.
|
||||
Vue.mixin({
|
||||
beforeCreate () {
|
||||
const options = this.$options
|
||||
// root component (vm)
|
||||
if (options.el) {
|
||||
// set external data of instance
|
||||
const dataOption = options.data
|
||||
const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}
|
||||
options.data = Object.assign(internalData, instance.data)
|
||||
// record instance by id
|
||||
instance.app = this
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @deprecated Just instance variable `weex.config`
|
||||
* Get instance config.
|
||||
* @return {object}
|
||||
*/
|
||||
Vue.prototype.$getConfig = function () {
|
||||
if (instance.app instanceof Vue) {
|
||||
return instance.config
|
||||
}
|
||||
}
|
||||
|
||||
return Vue
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate native module getter. Each native module has several
|
||||
* methods to call. And all the behaviors is instance-related. So
|
||||
* this getter will return a set of methods which additionally
|
||||
* send current instance id to native when called. Also the args
|
||||
* will be normalized into "safe" value. For example function arg
|
||||
* will be converted into a callback id.
|
||||
* @param {string} instanceId
|
||||
* @return {function}
|
||||
*/
|
||||
function genModuleGetter (instanceId) {
|
||||
const instance = instances[instanceId]
|
||||
return function (name) {
|
||||
const nativeModule = modules[name] || []
|
||||
const output = {}
|
||||
for (const methodName in nativeModule) {
|
||||
output[methodName] = (...args) => {
|
||||
const finalArgs = args.map(value => {
|
||||
return normalize(value, instance)
|
||||
})
|
||||
renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1)
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML5 Timer APIs. An important point is that the callback
|
||||
* will be converted into callback id when sent to native. So the
|
||||
* framework can make sure no side effect of the callback happened after
|
||||
* an instance destroyed.
|
||||
* @param {[type]} instanceId [description]
|
||||
* @param {[type]} moduleGetter [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function getInstanceTimer (instanceId, moduleGetter) {
|
||||
const instance = instances[instanceId]
|
||||
const timer = moduleGetter('timer')
|
||||
const timerAPIs = {
|
||||
setTimeout: (...args) => {
|
||||
const handler = function () {
|
||||
args[0](...args.slice(2))
|
||||
}
|
||||
timer.setTimeout(handler, args[1])
|
||||
return instance.callbackId.toString()
|
||||
},
|
||||
setInterval: (...args) => {
|
||||
const handler = function () {
|
||||
args[0](...args.slice(2))
|
||||
}
|
||||
timer.setInterval(handler, args[1])
|
||||
return instance.callbackId.toString()
|
||||
},
|
||||
clearTimeout: (n) => {
|
||||
timer.clearTimeout(n)
|
||||
},
|
||||
clearInterval: (n) => {
|
||||
timer.clearInterval(n)
|
||||
}
|
||||
}
|
||||
return timerAPIs
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a new function body with some global objects.
|
||||
* @param {object} globalObjects
|
||||
* @param {string} code
|
||||
* @return {any}
|
||||
*/
|
||||
function callFunction (globalObjects, body) {
|
||||
const globalKeys = []
|
||||
const globalValues = []
|
||||
for (const key in globalObjects) {
|
||||
globalKeys.push(key)
|
||||
globalValues.push(globalObjects[key])
|
||||
}
|
||||
globalKeys.push(body)
|
||||
|
||||
const result = new Function(...globalKeys)
|
||||
return result(...globalValues)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all type of values into "safe" format to send to native.
|
||||
* 1. A `function` will be converted into callback id.
|
||||
* 2. An `Element` object will be converted into `ref`.
|
||||
* The `instance` param is used to generate callback id and store
|
||||
* function if necessary.
|
||||
* @param {any} v
|
||||
* @param {object} instance
|
||||
* @return {any}
|
||||
*/
|
||||
function normalize (v, instance) {
|
||||
const type = typof(v)
|
||||
|
||||
switch (type) {
|
||||
case 'undefined':
|
||||
case 'null':
|
||||
return ''
|
||||
case 'regexp':
|
||||
return v.toString()
|
||||
case 'date':
|
||||
return v.toISOString()
|
||||
case 'number':
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'array':
|
||||
case 'object':
|
||||
if (v instanceof renderer.Element) {
|
||||
return v.ref
|
||||
}
|
||||
return v
|
||||
case 'function':
|
||||
instance.callbacks[++instance.callbackId] = v
|
||||
return instance.callbackId.toString()
|
||||
default:
|
||||
return JSON.stringify(v)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the exact type of an object by `toString()`. For example call
|
||||
* `toString()` on an array will be returned `[object Array]`.
|
||||
* @param {any} v
|
||||
* @return {string}
|
||||
*/
|
||||
function typof (v) {
|
||||
const s = Object.prototype.toString.call(v)
|
||||
return s.substring(8, s.length - 1).toLowerCase()
|
||||
}
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
// this entry is built and wrapped with a factory function
|
||||
// used to generate a fresh copy of Vue for every Weex instance.
|
||||
|
||||
import Vue from './runtime/index'
|
||||
|
||||
exports.Vue = Vue
|
||||
+2
@@ -1,7 +1,9 @@
|
||||
import Richtext from './richtext'
|
||||
import Transition from './transition'
|
||||
import TransitionGroup from './transition-group'
|
||||
|
||||
export default {
|
||||
Richtext,
|
||||
Transition,
|
||||
TransitionGroup
|
||||
}
|
||||
|
||||
+2
@@ -10,12 +10,14 @@ import {
|
||||
query,
|
||||
mustUseProp,
|
||||
isReservedTag,
|
||||
isRuntimeComponent,
|
||||
isUnknownElement
|
||||
} from 'weex/util/index'
|
||||
|
||||
// install platform specific utils
|
||||
Vue.config.mustUseProp = mustUseProp
|
||||
Vue.config.isReservedTag = isReservedTag
|
||||
Vue.config.isRuntimeComponent = isRuntimeComponent
|
||||
Vue.config.isUnknownElement = isUnknownElement
|
||||
|
||||
// install platform runtime directives and components
|
||||
|
||||
+8
-3
@@ -4,21 +4,26 @@ import { makeMap } from 'shared/util'
|
||||
|
||||
export const isReservedTag = makeMap(
|
||||
'template,script,style,element,content,slot,link,meta,svg,view,' +
|
||||
'a,div,img,image,text,span,richtext,input,switch,textarea,spinner,select,' +
|
||||
'slider,slider-neighbor,indicator,trisition,trisition-group,canvas,' +
|
||||
'a,div,img,image,text,span,input,switch,textarea,spinner,select,' +
|
||||
'slider,slider-neighbor,indicator,canvas,' +
|
||||
'list,cell,header,loading,loading-indicator,refresh,scrollable,scroller,' +
|
||||
'video,web,embed,tabbar,tabheader,datepicker,timepicker,marquee,countdown',
|
||||
true
|
||||
)
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// more flexable than web
|
||||
// more flexible than web
|
||||
export const canBeLeftOpenTag = makeMap(
|
||||
'web,spinner,switch,video,textarea,canvas,' +
|
||||
'indicator,marquee,countdown',
|
||||
true
|
||||
)
|
||||
|
||||
export const isRuntimeComponent = makeMap(
|
||||
'richtext,trisition,trisition-group',
|
||||
true
|
||||
)
|
||||
|
||||
export const isUnaryTag = makeMap(
|
||||
'embed,img,image,input,link,meta',
|
||||
true
|
||||
|
||||
+3
-1
@@ -27,7 +27,9 @@ type RenderBundle = {
|
||||
modules?: { [filename: string]: Array<string> };
|
||||
};
|
||||
|
||||
export function createBundleRendererCreator (createRenderer: () => Renderer) {
|
||||
export function createBundleRendererCreator (
|
||||
createRenderer: (options?: RenderOptions) => Renderer
|
||||
) {
|
||||
return function createBundleRenderer (
|
||||
bundle: string | RenderBundle,
|
||||
rendererOptions?: RenderOptions = {}
|
||||
|
||||
+18
-2
@@ -115,7 +115,7 @@ export function createBundleRunner (entry, files, basedir, runInNewContext) {
|
||||
// styles injected by vue-style-loader.
|
||||
initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}
|
||||
runner = evaluate(entry, sandbox)
|
||||
// On subsequent renders, __VUE_SSR_CONTEXT__ will not be avaialbe
|
||||
// On subsequent renders, __VUE_SSR_CONTEXT__ will not be available
|
||||
// to prevent cross-request pollution.
|
||||
delete sandbox.__VUE_SSR_CONTEXT__
|
||||
if (typeof runner !== 'function') {
|
||||
@@ -130,7 +130,23 @@ export function createBundleRunner (entry, files, basedir, runInNewContext) {
|
||||
if (initialContext._styles) {
|
||||
userContext._styles = deepClone(initialContext._styles)
|
||||
}
|
||||
resolve(runner(userContext))
|
||||
// #6353 after the app is resolved, if the userContext doesn't have a
|
||||
// styles property, it means the app doesn't have any lifecycle-injected
|
||||
// styles, so vue-style-loader never defined the styles getter.
|
||||
// just expose the same styles from the initialContext.
|
||||
const exposeStylesAndResolve = app => {
|
||||
if (!userContext.hasOwnProperty('styles')) {
|
||||
userContext.styles = initialContext.styles
|
||||
}
|
||||
resolve(app)
|
||||
}
|
||||
|
||||
const res = runner(userContext)
|
||||
if (typeof res.then === 'function') {
|
||||
res.then(exposeStylesAndResolve)
|
||||
} else {
|
||||
exposeStylesAndResolve(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
|
||||
import RenderStream from './render-stream'
|
||||
import TemplateRenderer from './template-renderer/index'
|
||||
import { createWriteFunction } from './write'
|
||||
import { createRenderFunction } from './render'
|
||||
import TemplateRenderer from './template-renderer/index'
|
||||
import type { ClientManifest } from './template-renderer/index'
|
||||
|
||||
export type Renderer = {
|
||||
@@ -18,7 +18,7 @@ type RenderCache = {
|
||||
};
|
||||
|
||||
export type RenderOptions = {
|
||||
modules?: Array<(vnode: VNode) => string>;
|
||||
modules?: Array<(vnode: VNode) => ?string>;
|
||||
directives?: Object;
|
||||
isUnaryTag?: Function;
|
||||
cache?: RenderCache;
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export class RenderContext {
|
||||
next: () => void;
|
||||
done: () => void;
|
||||
|
||||
modules: Array<() => ?string>;
|
||||
modules: Array<(node: VNode) => ?string>;
|
||||
directives: Object;
|
||||
isUnaryTag: (tag: string) => boolean;
|
||||
|
||||
|
||||
+102
-25
@@ -1,13 +1,21 @@
|
||||
/* @flow */
|
||||
|
||||
const { escape } = require('he')
|
||||
import {
|
||||
isDef,
|
||||
isUndef,
|
||||
isTrue
|
||||
} from 'shared/util'
|
||||
|
||||
import { escape } from 'web/server/util'
|
||||
import { SSR_ATTR } from 'shared/constants'
|
||||
import { RenderContext } from './render-context'
|
||||
import { compileToFunctions } from 'web/compiler/index'
|
||||
import { createComponentInstanceForVnode } from 'core/vdom/create-component'
|
||||
import { ssrCompileToFunctions } from 'web/server/compiler'
|
||||
import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
|
||||
|
||||
import { isDef, isUndef, isTrue } from 'shared/util'
|
||||
import {
|
||||
createComponent,
|
||||
createComponentInstanceForVnode
|
||||
} from 'core/vdom/create-component'
|
||||
|
||||
let warned = Object.create(null)
|
||||
const warnOnce = msg => {
|
||||
@@ -17,16 +25,13 @@ const warnOnce = msg => {
|
||||
}
|
||||
}
|
||||
|
||||
const compilationCache = Object.create(null)
|
||||
const normalizeRender = vm => {
|
||||
const { render, template } = vm.$options
|
||||
const { render, template, _scopeId } = vm.$options
|
||||
if (isUndef(render)) {
|
||||
if (template) {
|
||||
const renderFns = (
|
||||
compilationCache[template] ||
|
||||
(compilationCache[template] = compileToFunctions(template))
|
||||
)
|
||||
Object.assign(vm.$options, renderFns)
|
||||
Object.assign(vm.$options, ssrCompileToFunctions(template, {
|
||||
scopeId: _scopeId
|
||||
}))
|
||||
} else {
|
||||
throw new Error(
|
||||
`render function or template not defined in component: ${
|
||||
@@ -38,22 +43,24 @@ const normalizeRender = vm => {
|
||||
}
|
||||
|
||||
function renderNode (node, isRoot, context) {
|
||||
if (isDef(node.componentOptions)) {
|
||||
if (node.isString) {
|
||||
renderStringNode(node, context)
|
||||
} else if (isDef(node.componentOptions)) {
|
||||
renderComponent(node, isRoot, context)
|
||||
} else {
|
||||
if (isDef(node.tag)) {
|
||||
renderElement(node, isRoot, context)
|
||||
} else if (isTrue(node.isComment)) {
|
||||
context.write(
|
||||
`<!--${node.text}-->`,
|
||||
context.next
|
||||
)
|
||||
} else if (isDef(node.tag)) {
|
||||
renderElement(node, isRoot, context)
|
||||
} else if (isTrue(node.isComment)) {
|
||||
if (isDef(node.asyncFactory)) {
|
||||
// async component
|
||||
renderAsyncComponent(node, isRoot, context)
|
||||
} else {
|
||||
context.write(
|
||||
node.raw ? node.text : escape(String(node.text)),
|
||||
context.next
|
||||
)
|
||||
context.write(`<!--${node.text}-->`, context.next)
|
||||
}
|
||||
} else {
|
||||
context.write(
|
||||
node.raw ? node.text : escape(String(node.text)),
|
||||
context.next
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +168,75 @@ function renderComponentInner (node, isRoot, context) {
|
||||
renderNode(childNode, isRoot, context)
|
||||
}
|
||||
|
||||
function renderAsyncComponent (node, isRoot, context) {
|
||||
const factory = node.asyncFactory
|
||||
|
||||
const resolve = comp => {
|
||||
if (comp.__esModule && comp.default) {
|
||||
comp = comp.default
|
||||
}
|
||||
const { data, children, tag } = node.asyncMeta
|
||||
const nodeContext = node.asyncMeta.context
|
||||
const resolvedNode: any = createComponent(
|
||||
comp,
|
||||
data,
|
||||
nodeContext,
|
||||
children,
|
||||
tag
|
||||
)
|
||||
if (resolvedNode) {
|
||||
renderComponent(resolvedNode, isRoot, context)
|
||||
} else {
|
||||
reject()
|
||||
}
|
||||
}
|
||||
|
||||
const reject = err => {
|
||||
console.error(`[vue-server-renderer] error when rendering async component:\n`)
|
||||
if (err) console.error(err.stack)
|
||||
context.write(`<!--${node.text}-->`, context.next)
|
||||
}
|
||||
|
||||
if (factory.resolved) {
|
||||
resolve(factory.resolved)
|
||||
return
|
||||
}
|
||||
|
||||
let res
|
||||
try {
|
||||
res = factory(resolve, reject)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
if (res) {
|
||||
if (typeof res.then === 'function') {
|
||||
res.then(resolve, reject).catch(reject)
|
||||
} else {
|
||||
// new syntax in 2.3
|
||||
const comp = res.component
|
||||
if (comp && typeof comp.then === 'function') {
|
||||
comp.then(resolve, reject).catch(reject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderStringNode (el, context) {
|
||||
const { write, next } = context
|
||||
if (isUndef(el.children) || el.children.length === 0) {
|
||||
write(el.open + (el.close || ''), next)
|
||||
} else {
|
||||
const children: Array<VNode> = el.children
|
||||
context.renderStates.push({
|
||||
type: 'Element',
|
||||
rendered: 0,
|
||||
total: children.length,
|
||||
endTag: el.close, children
|
||||
})
|
||||
write(el.open, next)
|
||||
}
|
||||
}
|
||||
|
||||
function renderElement (el, isRoot, context) {
|
||||
const { write, next } = context
|
||||
|
||||
@@ -270,7 +346,7 @@ function renderStartingTag (node: VNode, context) {
|
||||
}
|
||||
|
||||
export function createRenderFunction (
|
||||
modules: Array<Function>,
|
||||
modules: Array<(node: VNode) => ?string>,
|
||||
directives: Object,
|
||||
isUnaryTag: Function,
|
||||
cache: any
|
||||
@@ -289,6 +365,7 @@ export function createRenderFunction (
|
||||
isUnaryTag, modules, directives,
|
||||
cache
|
||||
})
|
||||
installSSRHelpers(component)
|
||||
normalizeRender(component)
|
||||
renderNode(component._render(), true, context)
|
||||
}
|
||||
|
||||
+5
-5
@@ -60,7 +60,7 @@ export default class TemplateRenderer {
|
||||
if (options.clientManifest) {
|
||||
const clientManifest = this.clientManifest = options.clientManifest
|
||||
this.publicPath = clientManifest.publicPath.replace(/\/$/, '')
|
||||
// preload/prefetch drectives
|
||||
// preload/prefetch directives
|
||||
this.preloadFiles = clientManifest.initial
|
||||
this.prefetchFiles = clientManifest.async
|
||||
// initial async chunk mapping
|
||||
@@ -181,7 +181,7 @@ export default class TemplateRenderer {
|
||||
}
|
||||
return this.prefetchFiles.map(file => {
|
||||
if (!alreadyRendered(file)) {
|
||||
return `<link rel="prefetch" href="${this.publicPath}/${file}" as="script">`
|
||||
return `<link rel="prefetch" href="${this.publicPath}/${file}">`
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
@@ -209,7 +209,7 @@ export default class TemplateRenderer {
|
||||
const async = this.getUsedAsyncFiles(context)
|
||||
const needed = [initial[0]].concat(async || [], initial.slice(1))
|
||||
return needed.filter(isJS).map(file => {
|
||||
return `<script src="${this.publicPath}/${file}"></script>`
|
||||
return `<script src="${this.publicPath}/${file}" defer></script>`
|
||||
}).join('')
|
||||
} else {
|
||||
return ''
|
||||
@@ -217,7 +217,7 @@ export default class TemplateRenderer {
|
||||
}
|
||||
|
||||
getUsedAsyncFiles (context: Object): ?Array<string> {
|
||||
if (!context._mappedfiles && context._registeredComponents && this.mapFiles) {
|
||||
if (!context._mappedFiles && context._registeredComponents && this.mapFiles) {
|
||||
context._mappedFiles = this.mapFiles(Array.from(context._registeredComponents))
|
||||
}
|
||||
return context._mappedFiles
|
||||
@@ -242,7 +242,7 @@ function getPreloadType (ext: string): string {
|
||||
} else if (/woff2?|ttf|otf|eot/.test(ext)) {
|
||||
return 'font'
|
||||
} else {
|
||||
// not exhausting all possbilities here, but above covers common cases
|
||||
// not exhausting all possibilities here, but above covers common cases
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
+45
-12
@@ -17,11 +17,16 @@ export function isTrue (v: any): boolean %checks {
|
||||
export function isFalse (v: any): boolean %checks {
|
||||
return v === false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if value is primitive
|
||||
*/
|
||||
export function isPrimitive (value: any): boolean %checks {
|
||||
return typeof value === 'string' || typeof value === 'number'
|
||||
return (
|
||||
typeof value === 'string' ||
|
||||
typeof value === 'number' ||
|
||||
typeof value === 'boolean'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +52,14 @@ export function isRegExp (v: any): boolean {
|
||||
return _toString.call(v) === '[object RegExp]'
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if val is a valid array index.
|
||||
*/
|
||||
export function isValidArrayIndex (val: any): boolean {
|
||||
const n = parseFloat(val)
|
||||
return n >= 0 && Math.floor(n) === n && isFinite(val)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value to a string that is actually rendered.
|
||||
*/
|
||||
@@ -90,6 +103,11 @@ export function makeMap (
|
||||
*/
|
||||
export const isBuiltInTag = makeMap('slot,component', true)
|
||||
|
||||
/**
|
||||
* Check if a attribute is a reserved attribute.
|
||||
*/
|
||||
export const isReservedAttribute = makeMap('key,ref,slot,is')
|
||||
|
||||
/**
|
||||
* Remove an item from an array
|
||||
*/
|
||||
@@ -139,12 +157,9 @@ export const capitalize = cached((str: string): string => {
|
||||
/**
|
||||
* Hyphenate a camelCase string.
|
||||
*/
|
||||
const hyphenateRE = /([^-])([A-Z])/g
|
||||
const hyphenateRE = /\B([A-Z])/g
|
||||
export const hyphenate = cached((str: string): string => {
|
||||
return str
|
||||
.replace(hyphenateRE, '$1-$2')
|
||||
.replace(hyphenateRE, '$1-$2')
|
||||
.toLowerCase()
|
||||
return str.replace(hyphenateRE, '-$1').toLowerCase()
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -202,13 +217,15 @@ export function toObject (arr: Array<any>): Object {
|
||||
|
||||
/**
|
||||
* Perform no operation.
|
||||
* Stubbing args to make Flow happy without leaving useless transpiled code
|
||||
* with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)
|
||||
*/
|
||||
export function noop () {}
|
||||
export function noop (a?: any, b?: any, c?: any) {}
|
||||
|
||||
/**
|
||||
* Always return false.
|
||||
*/
|
||||
export const no = () => false
|
||||
export const no = (a?: any, b?: any, c?: any) => false
|
||||
|
||||
/**
|
||||
* Return same value
|
||||
@@ -228,15 +245,31 @@ export function genStaticKeys (modules: Array<ModuleOptions>): string {
|
||||
* Check if two values are loosely equal - that is,
|
||||
* if they are plain objects, do they have the same shape?
|
||||
*/
|
||||
export function looseEqual (a: mixed, b: mixed): boolean {
|
||||
export function looseEqual (a: any, b: any): boolean {
|
||||
if (a === b) return true
|
||||
const isObjectA = isObject(a)
|
||||
const isObjectB = isObject(b)
|
||||
if (isObjectA && isObjectB) {
|
||||
try {
|
||||
return JSON.stringify(a) === JSON.stringify(b)
|
||||
const isArrayA = Array.isArray(a)
|
||||
const isArrayB = Array.isArray(b)
|
||||
if (isArrayA && isArrayB) {
|
||||
return a.length === b.length && a.every((e, i) => {
|
||||
return looseEqual(e, b[i])
|
||||
})
|
||||
} else if (!isArrayA && !isArrayB) {
|
||||
const keysA = Object.keys(a)
|
||||
const keysB = Object.keys(b)
|
||||
return keysA.length === keysB.length && keysA.every(key => {
|
||||
return looseEqual(a[key], b[key])
|
||||
})
|
||||
} else {
|
||||
/* istanbul ignore next */
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
// possible circular reference
|
||||
return a === b
|
||||
/* istanbul ignore next */
|
||||
return false
|
||||
}
|
||||
} else if (!isObjectA && !isObjectB) {
|
||||
return String(a) === String(b)
|
||||
|
||||
+13
-5
@@ -6,10 +6,15 @@ type Constructor = {
|
||||
}
|
||||
|
||||
export type Component = typeof Vue | ComponentOptions<Vue> | FunctionalComponentOptions;
|
||||
|
||||
interface EsModuleComponent {
|
||||
default: Component
|
||||
}
|
||||
|
||||
export type AsyncComponent = (
|
||||
resolve: (component: Component) => void,
|
||||
reject: (reason?: any) => void
|
||||
) => Promise<Component> | Component | void;
|
||||
) => Promise<Component | EsModuleComponent> | Component | void;
|
||||
|
||||
export interface ComponentOptions<V extends Vue> {
|
||||
data?: Object | ((this: V) => Object);
|
||||
@@ -42,7 +47,7 @@ export interface ComponentOptions<V extends Vue> {
|
||||
filters?: { [key: string]: Function };
|
||||
|
||||
provide?: Object | (() => Object);
|
||||
inject?: { [key: string]: string | symbol } | Array<string>;
|
||||
inject?: { [key: string]: string | symbol } | string[];
|
||||
|
||||
model?: {
|
||||
prop?: string;
|
||||
@@ -54,13 +59,16 @@ export interface ComponentOptions<V extends Vue> {
|
||||
name?: string;
|
||||
extends?: ComponentOptions<Vue> | typeof Vue;
|
||||
delimiters?: [string, string];
|
||||
comments?: boolean;
|
||||
inheritAttrs?: boolean;
|
||||
}
|
||||
|
||||
export interface FunctionalComponentOptions {
|
||||
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
|
||||
functional: boolean;
|
||||
render(this: never, createElement: CreateElement, context: RenderContext): VNode;
|
||||
name?: string;
|
||||
props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] };
|
||||
inject?: { [key: string]: string | symbol } | string[];
|
||||
functional: boolean;
|
||||
render(this: never, createElement: CreateElement, context: RenderContext): VNode | void;
|
||||
}
|
||||
|
||||
export interface RenderContext {
|
||||
|
||||
+6
@@ -42,7 +42,11 @@ export declare class Vue {
|
||||
readonly $slots: { [key: string]: VNode[] };
|
||||
readonly $scopedSlots: { [key: string]: ScopedSlot };
|
||||
readonly $isServer: boolean;
|
||||
readonly $ssrContext: any;
|
||||
readonly $props: any;
|
||||
readonly $vnode: VNode;
|
||||
readonly $attrs: { [key: string] : string };
|
||||
readonly $listeners: { [key: string]: Function | Array<Function> };
|
||||
|
||||
$mount(elementOrSelector?: Element | String, hydrating?: boolean): this;
|
||||
$forceUpdate(): void;
|
||||
@@ -74,6 +78,7 @@ export declare class Vue {
|
||||
productionTip: boolean;
|
||||
performance: boolean;
|
||||
errorHandler(err: Error, vm: Vue, info: string): void;
|
||||
warnHandler(msg: string, vm: Vue, trace: string): void;
|
||||
ignoredElements: string[];
|
||||
keyCodes: { [key: string]: number };
|
||||
}
|
||||
@@ -94,6 +99,7 @@ export declare class Vue {
|
||||
static component(id: string, definition?: Component | AsyncComponent): typeof Vue;
|
||||
|
||||
static use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void;
|
||||
static use(plugin: PluginObject<any> | PluginFunction<any>, ...options: any[]): void;
|
||||
static mixin(mixin: typeof Vue | ComponentOptions<Vue>): void;
|
||||
static compile(template: string): {
|
||||
render(createElement: typeof Vue.prototype.$createElement): VNode;
|
||||
|
||||
Reference in New Issue
Block a user