diff --git a/config/webpack.dev.conf.js b/config/webpack.dev.conf.js new file mode 100644 index 000000000..d9b990e07 --- /dev/null +++ b/config/webpack.dev.conf.js @@ -0,0 +1,35 @@ +var webpack = require('webpack') + +module.exports = { + entry: './index.js', + output: { + path: './', + publicPath: '/', + filename: 'build.js' + }, + devtool: 'source-map', + module: { + preLoaders: [{ + test: /\.js$/, + exclude: /node_modules/, + loader: 'eslint-loader' + }], + loaders: [{ + test: /\.js$/, + exclude: /node_modules|vue\/dist/, + loader: 'babel', + query: { + presets: ['es2015'] + } + }] + }, + devServer: { + contentBase: './', + port: 8080, + hot: true, + inline: true + }, + plugins: [ + new webpack.HotModuleReplacementPlugin() + ] +} diff --git a/package.json b/package.json index 7fdf816fa..6bf5fd66c 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "rollup-plugin-babel": "^2.4.0", "rollup-plugin-replace": "^1.1.0", "uglify-js": "^2.6.1", - "vue": "^0.12.0", + "vue": "^1.0.21", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1" }, @@ -84,6 +84,7 @@ "coolkids": "VUE_I18N_TYPE=sauce SAUCE=batch1 karma start config/karma.conf.js", "coverage": "VUE_I18N_TYPE=coverage karma start config/karma.conf.js", "coveralls": "VUE_I18N_TYPE=coveralls karma start config/karma.conf.js", + "proto": "webpack-dev-server --quite --config config/webpack.dev.conf.js --host 0.0.0.0", "dev": "webpack-dev-server --quiet --config config/webpack.test.conf.js", "e2e": "webpack-dev-server --quiet --config config/webpack.e2e.conf.js & mocha -t 20000 --compilers js:espower-babel/guess test/e2e/test.js && kill $! || (kill $! && exit 1)", "ie": "VUE_I18N_TYPE=sauce SAUCE=batch2 karma start config/karma.conf.js", diff --git a/src/config.js b/src/config.js new file mode 100644 index 000000000..73c4fecac --- /dev/null +++ b/src/config.js @@ -0,0 +1,30 @@ +import { getWatcher, getDep } from './util' + +export default function (Vue, langVM) { + const Watcher = getWatcher(langVM) + const Dep = getDep(langVM) + + function makeComputedGetter (getter, owner) { + let watcher = new Watcher(owner, getter, null, { + lazy: true + }) + + return function computedGetter () { + if (watcher.dirty) { + watcher.evaluate() + } + if (Dep.target) { + watcher.depend() + } + return watcher.value + } + } + + // define Vue.config.lang configration + Object.defineProperty(Vue.config, 'lang', { + enumerable: true, + configurable: true, + get: makeComputedGetter(() => { return langVM.lang }, langVM), + set: Vue.util.bind((val) => { langVM.lang = val }, langVM) + }) +} diff --git a/src/index.js b/src/index.js index 2edf19082..c7370cdcb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,9 @@ -import util, { warn } from './util' -import extend from './extend' +import { warn } from './util' +import Override from './override' +import Config from './config' +import Extend from './extend' + +let langVM // singleton /** @@ -15,26 +19,20 @@ function plugin (Vue, opts = { lang: 'en', locales: {} }) { return } - defineConfig(Vue.config, opts.lang) - extend(Vue, opts.locales) -} - + setupLangVM(Vue, opts.lang) -/** - * defineConfig - * - * This function define `lang` property to `Vue.config`. - * - * @param {Object} config - * @param {String} lang - * @private - */ + Override(Vue, langVM) + Config(Vue, langVM) + Extend(Vue, opts.locales) +} -function defineConfig (config, lang) { - Object.defineProperty(config, 'lang', { - get: () => { return lang }, - set: (val) => { lang = val } - }) +function setupLangVM (Vue, lang) { + const silent = Vue.config.silent + Vue.config.silent = true + if (!langVM) { + langVM = new Vue({ data: { lang: lang } }) + } + Vue.config.silent = silent } plugin.version = '2.4.1' diff --git a/src/override.js b/src/override.js new file mode 100644 index 000000000..df359293b --- /dev/null +++ b/src/override.js @@ -0,0 +1,40 @@ +export default function (Vue, langVM) { + // override _init + const init = Vue.prototype._init + Vue.prototype._init = function (options) { + options = options || {} + let root = options._parent || options.parent || this + let lang = root.$lang + + if (lang) { + this.$lang = lang + } else { + this.$lang = langVM + } + + this._langUnwatch = this.$lang.$watch('lang', (a, b) => { + update(this) + }) + + init.call(this, options) + } + + // override _destroy + const destroy = Vue.prototype._destroy + Vue.prototype._destroy = function () { + if (this._langUnwatch) { + this._langUnwatch() + this._langUnwatch = null + } + + this.$lang = null + destroy.apply(this, arguments) + } +} + +function update (vm) { + let i = vm._watchers.length + while (i--) { + vm._watchers[i].update(true) // shallow updates + } +} diff --git a/test/specs/i18n.js b/test/specs/i18n.js index 879936ba2..2e2b158de 100644 --- a/test/specs/i18n.js +++ b/test/specs/i18n.js @@ -291,15 +291,21 @@ describe('i18n', () => { }) context('ja', () => { - it('should translate with japanese', () => { + it('should translate with japanese', (done) => { Vue.config.lang = 'ja' - assert(vm.$t('message.hello') === locales.ja.message.hello) + Vue.nextTick(() => { + assert(vm.$t('message.hello') === locales.ja.message.hello) + done() + }) }) context('en', () => { - it('should translate with english', () => { + it('should translate with english', (done) => { Vue.config.lang = 'en' - assert(vm.$t('message.hello') === locales.en.message.hello) + Vue.nextTick(() => { + assert(vm.$t('message.hello') === locales.en.message.hello) + done() + }) }) }) })