Browse Source

adjust build setup for better production minification

Evan You 11 năm trước cách đây
mục cha
commit
0d3b26455d

+ 8 - 0
build/banner.js

@@ -0,0 +1,8 @@
+var version =
+  process.env.VUE_VERSION ||
+  require('../package.json').version
+
+module.exports =
+  'Vue.js v' + version + '\n' +
+  '(c) ' + new Date().getFullYear() + ' Evan You\n' +
+  'Released under the MIT License.'

+ 21 - 53
build/grunt-tasks/build.js

@@ -1,5 +1,5 @@
 /**
- * Build, update component.json, uglify, and report size.
+ * Build and report size.
  */
 
 module.exports = function (grunt) {
@@ -9,66 +9,34 @@ module.exports = function (grunt) {
     var fs = require('fs')
     var zlib = require('zlib')
     var webpack = require('webpack')
-    var uglifyjs = require('uglify-js')
+    var devConfig = require('../webpack.build.dev.config')
+    var prodConfig = require('../webpack.build.prod.config')
 
-    var banner =
-        '/**\n' +
-        ' * Vue.js v' + grunt.config.get('version') + '\n' +
-        ' * (c) ' + new Date().getFullYear() + ' Evan You\n' +
-        ' * Released under the MIT License.\n' +
-        ' */\n'
-
-    // build
-    webpack({
-      entry: './src/vue',
-      output: {
-        path: './dist',
-        filename: 'vue.js',
-        library: 'Vue',
-        libraryTarget: 'umd'
-      },
-      plugins: [
-        new webpack.BannerPlugin(banner, { raw: true })
-      ]
-    }, function (err, stats) {
+    webpack(devConfig, function (err, stats) {
       if (err) return done(err)
-      minify()
+      report('dist/vue.js')
+      webpack(prodConfig, function (err, stats) {
+        if (err) return done(err)
+        report('dist/vue.min.js')
+        zip()
+      })
     })
 
-    function minify () {
-      var js = fs.readFileSync('dist/vue.js', 'utf-8')
-      report('dist/vue.js', js)
-      // uglify
-      var result = uglifyjs.minify(js, {
-        fromString: true,
-        output: {
-          comments: /License/
-        },
-        compress: {
-          pure_funcs: [
-            'require',
-            '_.log',
-            '_.warn',
-            '_.assertAsset',
-            'enableDebug'
-          ]
-        }
-      })
-      // var min = grunt.config.get('banner') + result.code
-      write('dist/vue.min.js', result.code)
-      // report gzip size
-      zlib.gzip(result.code, function (err, buf) {
-        write('dist/vue.min.js.gz', buf)
-        done(err)
+    function zip () {
+      fs.readFile('dist/vue.min.js', function (err, buf) {
+        if (err) return done(err)
+        zlib.gzip(buf, function (err, buf) {
+          if (err) return done(err)
+          report('dist/vue.min.js.gz', buf)
+          done()
+        })
       })
     }
 
-    function write (path, file) {
-      fs.writeFileSync(path, file)
-      report(path, file)
-    }
-
     function report (path, file) {
+      if (!file) {
+        file = fs.readFileSync(path)
+      }
       console.log(
         blue(path + ': ') +
         (file.length / 1024).toFixed(2) + 'kb'

+ 3 - 2
build/grunt-tasks/release.js

@@ -9,7 +9,7 @@ module.exports = function (grunt) {
    */
 
   grunt.registerTask('version', function (version) {
-    var manifests = ['package', 'bower', 'component']
+    var manifests = ['package', 'component']
     manifests.forEach(function (file) {
       file = file + '.json'
       var json = grunt.file.read(file)
@@ -62,7 +62,8 @@ module.exports = function (grunt) {
     }).question('Releasing version ' + next + '. Continue? (Y/n)', function (answer) {
       if (!answer || answer.toLowerCase() === 'y') {
         console.log(blue('Releasing: ' + next))
-        grunt.config.set('version', next)
+        // set version in env
+        process.env.VUE_VERSION = next
         grunt.task.run([
           'eslint',
           'cover',

+ 0 - 0
build/saucelabs-config.js → build/saucelabs.config.js


+ 20 - 0
build/webpack.build.dev.config.js

@@ -0,0 +1,20 @@
+var webpack = require('webpack')
+var banner = require('./banner')
+
+module.exports = {
+  entry: './src/vue',
+  output: {
+    path: './dist',
+    filename: 'vue.js',
+    library: 'Vue',
+    libraryTarget: 'umd'
+  },
+  plugins: [
+    new webpack.BannerPlugin(banner),
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: '"development"'
+      }
+    })
+  ]
+}

+ 25 - 0
build/webpack.build.prod.config.js

@@ -0,0 +1,25 @@
+var webpack = require('webpack')
+var banner = require('./banner')
+
+module.exports = {
+  entry: './src/vue',
+  output: {
+    path: './dist',
+    filename: 'vue.min.js',
+    library: 'Vue',
+    libraryTarget: 'umd'
+  },
+  plugins: [
+    new webpack.BannerPlugin(banner),
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: '"production"'
+      }
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {
+        warnings: false
+      }
+    })
+  ]
+}

+ 0 - 0
build/webpack-dev-config.js → build/webpack.dev.config.js


+ 0 - 0
build/webpack-test-config.js → build/webpack.test.config.js


+ 1 - 1
gruntfile.js

@@ -1,4 +1,4 @@
-var sauceConfig = require('./build/saucelabs-config')
+var sauceConfig = require('./build/saucelabs.config.js')
 
 module.exports = function (grunt) {
 

+ 9 - 1
package.json

@@ -18,7 +18,15 @@
   "homepage": "http://vuejs.org",
   "scripts": {
     "test": "grunt ci",
-    "dev": "webpack --watch --config build/webpack-dev-config.js & webpack --watch --config build/webpack-test-config.js"
+    "dev": "webpack --watch --config build/webpack.dev.config.js & webpack --watch --config build/webpack.test.config.js"
+  },
+  "dependencies": {
+    "envify": "^3.4.0"
+  },
+  "browserify": {
+    "transform": [
+      "envify"
+    ]
   },
   "devDependencies": {
     "casperjs": "^1.1.0-beta3",

+ 3 - 1
src/api/lifecycle.js

@@ -13,7 +13,9 @@ var compiler = require('../compiler')
 
 exports.$mount = function (el) {
   if (this._isCompiled) {
-    _.warn('$mount() should be called only once.')
+    process.env.NODE_ENV !== 'production' && _.warn(
+      '$mount() should be called only once.'
+    )
     return
   }
   el = _.query(el)

+ 1 - 1
src/batcher.js

@@ -71,7 +71,7 @@ exports.push = function (job) {
       has[id]++
       // detect possible infinite update loops
       if (has[id] > config._maxUpdateCount) {
-        _.warn(
+        process.env.NODE_ENV !== 'production' && _.warn(
           'You may have an infinite update loop for the ' +
           'watcher with expression: "' + job.expression + '".'
         )

+ 7 - 4
src/compiler/compile-props.js

@@ -30,10 +30,11 @@ module.exports = function compileProps (el, propOptions) {
     // so we need to camelize the path here
     path = _.camelize(name.replace(dataAttrRE, ''))
     if (!identRE.test(path)) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Invalid prop key: "' + name + '". Prop keys ' +
         'must be valid identifiers.'
       )
+      continue
     }
     value = el.getAttribute(_.hyphenate(name))
     // create a prop descriptor
@@ -68,7 +69,7 @@ module.exports = function compileProps (el, propOptions) {
           if (settablePathRE.test(prop.parentPath)) {
             prop.mode = propBindingModes.TWO_WAY
           } else {
-            _.warn(
+            process.env.NODE_ENV !== 'production' && _.warn(
               'Cannot bind two-way prop with non-settable ' +
               'parent path: ' + prop.parentPath
             )
@@ -76,7 +77,9 @@ module.exports = function compileProps (el, propOptions) {
         }
       }
     } else if (options && options.required) {
-      _.warn('Missing required prop: ' + name)
+      process.env.NODE_ENV !== 'production' && _.warn(
+        'Missing required prop: ' + name
+      )
     }
     props.push(prop)
   }
@@ -120,7 +123,7 @@ function makePropsLinkFn (props) {
             vm._bindDir('prop', el, prop, propDef)
           }
         } else {
-          _.warn(
+          process.env.NODE_ENV !== 'production' && _.warn(
             'Cannot bind dynamic prop on a root instance' +
             ' with no parent: ' + prop.name + '="' +
             prop.raw + '"'

+ 3 - 1
src/compiler/compile.js

@@ -522,7 +522,9 @@ function compileDirectives (elOrAttrs, options) {
     if (name.indexOf(config.prefix) === 0) {
       dirName = name.slice(config.prefix.length)
       dirDef = resolveAsset(options, 'directives', dirName)
-      _.assertAsset(dirDef, 'directive', dirName)
+      if (process.env.NODE_ENV !== 'production') {
+        _.assertAsset(dirDef, 'directive', dirName)
+      }
       if (dirDef) {
         dirs.push({
           name: dirName,

+ 6 - 4
src/compiler/transclude.js

@@ -59,15 +59,13 @@ exports.transclude = function (el, options) {
 function transcludeTemplate (el, options) {
   var template = options.template
   var frag = templateParser.parse(template, true)
-  if (!frag) {
-    _.warn('Invalid template option: ' + template)
-  } else {
+  if (frag) {
     var replacer = frag.firstChild
     var tag = replacer.tagName && replacer.tagName.toLowerCase()
     if (options.replace) {
       /* istanbul ignore if */
       if (el === document.body) {
-        _.warn(
+        process.env.NODE_ENV !== 'production' && _.warn(
           'You are mounting an instance with a template to ' +
           '<body>. This will replace <body> entirely. You ' +
           'should probably use `replace: false` here.'
@@ -96,6 +94,10 @@ function transcludeTemplate (el, options) {
       el.appendChild(frag)
       return el
     }
+  } else {
+    process.env.NODE_ENV !== 'production' && _.warn(
+      'Invalid template option: ' + template
+    )
   }
 }
 

+ 1 - 1
src/directives/component.js

@@ -50,7 +50,7 @@ module.exports = {
         this.transMode = this._checkParam('transition-mode')
       }
     } else {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Do not create a component that only contains ' +
         'a single other component - they will be mounted to ' +
         'the same element and cause conflict. Wrap it with ' +

+ 2 - 2
src/directives/if.js

@@ -25,11 +25,11 @@ module.exports = {
         true
       )
     } else {
-      this.invalid = true
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'v-if="' + this.expression + '" cannot be ' +
         'used on an instance root element.'
       )
+      this.invalid = true
     }
   },
 

+ 4 - 2
src/directives/model/index.js

@@ -29,7 +29,7 @@ module.exports = {
     // friendly warning...
     this.checkFilters()
     if (this.hasRead && !this.hasWrite) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'It seems you are using a read-only filter with ' +
         'v-model. You might want to use a two-way filter ' +
         'to ensure correct behavior.'
@@ -45,7 +45,9 @@ module.exports = {
     } else if (tag === 'TEXTAREA') {
       handler = handlers.text
     } else {
-      _.warn('v-model does not support element type: ' + tag)
+      process.env.NODE_ENV !== 'production' && _.warn(
+        'v-model does not support element type: ' + tag
+      )
       return
     }
     handler.bind.call(this)

+ 3 - 1
src/directives/model/select.js

@@ -82,7 +82,9 @@ function initOptions (expression) {
       buildOptions(self.el, value)
       self.forceUpdate()
     } else {
-      _.warn('Invalid options value for v-model: ' + value)
+      process.env.NODE_ENV !== 'production' && _.warn(
+        'Invalid options value for v-model: ' + value
+      )
     }
   }
   this.optionWatcher = new Watcher(

+ 1 - 1
src/directives/on.js

@@ -21,7 +21,7 @@ module.exports = {
 
   update: function (handler) {
     if (typeof handler !== 'function') {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Directive "v-on:' + this.expression + '" ' +
         'expects a function value.'
       )

+ 1 - 1
src/directives/ref.js

@@ -7,7 +7,7 @@ module.exports = {
   bind: function () {
     var vm = this.el.__vue__
     if (!vm) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'v-ref should only be used on a component root element.'
       )
       return

+ 10 - 6
src/directives/repeat.js

@@ -53,7 +53,7 @@ module.exports = {
 
   checkIf: function () {
     if (_.attr(this.el, 'if') !== null) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Don\'t use v-if with v-repeat. ' +
         'Use v-show or the "filterBy" filter instead.'
       )
@@ -155,9 +155,11 @@ module.exports = {
     }
     var id = this.ctorGetter.call(context, context)
     var Ctor = _.resolveAsset(this.vm.$options, 'components', id)
-    _.assertAsset(Ctor, 'component', id)
+    if (process.env.NODE_ENV !== 'production') {
+      _.assertAsset(Ctor, 'component', id)
+    }
     if (!Ctor.options) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Async resolution is not supported for v-repeat ' +
         '+ dynamic component. (component: ' + id + ')'
       )
@@ -380,7 +382,7 @@ module.exports = {
     ) {
       vm.$watch(alias || '$value', function (val) {
         if (dir.filters) {
-          _.warn(
+          process.env.NODE_ENV !== 'production' && _.warn(
             'You seem to be mutating the $value reference of ' +
             'a v-repeat instance (likely through v-model) ' +
             'and filtering the v-repeat at the same time. ' +
@@ -449,7 +451,9 @@ module.exports = {
       if (!cache[id]) {
         cache[id] = vm
       } else if (!primitive && idKey !== '$index') {
-        _.warn('Duplicate track-by key in v-repeat: ' + id)
+        process.env.NODE_ENV !== 'production' && _.warn(
+          'Duplicate track-by key in v-repeat: ' + id
+        )
       }
     } else {
       id = this.id
@@ -457,7 +461,7 @@ module.exports = {
         if (data[id] === null) {
           data[id] = vm
         } else {
-          _.warn(
+          process.env.NODE_ENV !== 'production' && _.warn(
             'Duplicate objects are not supported in v-repeat ' +
             'when using components or transitions.'
           )

+ 3 - 1
src/element-directives/partial.js

@@ -45,7 +45,9 @@ module.exports = {
 
   insert: function (id) {
     var partial = _.resolveAsset(this.vm.$options, 'partials', id)
-    _.assertAsset(partial, 'partial', id)
+    if (process.env.NODE_ENV !== 'production') {
+      _.assertAsset(partial, 'partial', id)
+    }
     if (partial) {
       var frag = templateParser.parse(partial, true)
       // cache partials based on constructor id.

+ 1 - 1
src/instance/events.js

@@ -56,7 +56,7 @@ function register (vm, action, key, handler, options) {
     if (method) {
       vm[action](key, method, options)
     } else {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Unknown method: "' + handler + '" when ' +
         'registering callback for ' + action +
         ': "' + key + '".'

+ 7 - 3
src/instance/misc.js

@@ -18,7 +18,9 @@ exports._applyFilters = function (value, oldValue, filters, write) {
   for (i = 0, l = filters.length; i < l; i++) {
     filter = filters[i]
     fn = _.resolveAsset(this.$options, 'filters', filter.name)
-    _.assertAsset(fn, 'filter', filter.name)
+    if (process.env.NODE_ENV !== 'production') {
+      _.assertAsset(fn, 'filter', filter.name)
+    }
     if (!fn) continue
     fn = write ? fn.write : (fn.read || fn)
     if (typeof fn !== 'function') continue
@@ -50,7 +52,9 @@ exports._applyFilters = function (value, oldValue, filters, write) {
 
 exports._resolveComponent = function (id, cb) {
   var factory = _.resolveAsset(this.$options, 'components', id)
-  _.assertAsset(factory, 'component', id)
+  if (process.env.NODE_ENV !== 'production') {
+    _.assertAsset(factory, 'component', id)
+  }
   // async component factory
   if (!factory.options) {
     if (factory.resolved) {
@@ -73,7 +77,7 @@ exports._resolveComponent = function (id, cb) {
           cbs[i](res)
         }
       }, function reject (reason) {
-        _.warn(
+        process.env.NODE_ENV !== 'production' && _.warn(
           'Failed to resolve async component: ' + id + '. ' +
           (reason ? '\nReason: ' + reason : '')
         )

+ 1 - 1
src/instance/scope.js

@@ -28,7 +28,7 @@ exports._initProps = function () {
   var el = options.el
   var props = options.props
   if (props && !el) {
-    _.warn(
+    process.env.NODE_ENV !== 'production' && _.warn(
       'Props will not be compiled if no `el` option is ' +
       'provided at instantiation.'
     )

+ 5 - 3
src/parsers/expression.js

@@ -106,7 +106,7 @@ function restore (str, i) {
 
 function compileExpFns (exp, needSet) {
   if (improperKeywordsRE.test(exp)) {
-    _.warn(
+    process.env.NODE_ENV !== 'production' && _.warn(
       'Avoid using reserved keywords in expression: ' + exp
     )
   }
@@ -175,7 +175,7 @@ function makeGetter (body) {
   try {
     return new Function('scope', 'return ' + body + ';')
   } catch (e) {
-    _.warn(
+    process.env.NODE_ENV !== 'production' && _.warn(
       'Invalid expression. ' +
       'Generated function body: ' + body
     )
@@ -200,7 +200,9 @@ function makeSetter (body) {
   try {
     return new Function('scope', 'value', body + '=value;')
   } catch (e) {
-    _.warn('Invalid setter function body: ' + body)
+    process.env.NODE_ENV !== 'production' && _.warn(
+      'Invalid setter function body: ' + body
+    )
   }
 }
 

+ 3 - 3
src/parsers/path.js

@@ -299,9 +299,9 @@ exports.set = function (obj, path, val) {
     if (i < l - 1) {
       obj = obj[key]
       if (!_.isObject(obj)) {
+        warnNonExistent(path)
         obj = {}
         last.$add(key, obj)
-        warnNonExistent(path)
       }
     } else {
       if (_.isArray(obj)) {
@@ -309,8 +309,8 @@ exports.set = function (obj, path, val) {
       } else if (key in obj) {
         obj[key] = val
       } else {
-        obj.$add(key, val)
         warnNonExistent(path)
+        obj.$add(key, val)
       }
     }
   }
@@ -318,7 +318,7 @@ exports.set = function (obj, path, val) {
 }
 
 function warnNonExistent (path) {
-  _.warn(
+  process.env.NODE_ENV !== 'production' && _.warn(
     'You are setting a non-existent path "' + path.raw + '" ' +
     'on a vm instance. Consider pre-initializing the property ' +
     'with the "data" option for more reliable reactivity ' +

+ 2 - 2
src/util/component.js

@@ -87,7 +87,7 @@ exports.assertProp = function (prop, value) {
     }
   }
   if (!valid) {
-    _.warn(
+    process.env.NODE_ENV !== 'production' && _.warn(
       'Invalid prop: type check failed for ' +
       prop.path + '="' + prop.raw + '".' +
       ' Expected ' + formatType(expectedType) +
@@ -98,7 +98,7 @@ exports.assertProp = function (prop, value) {
   var validator = options.validator
   if (validator) {
     if (!validator.call(null, value)) {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'Invalid prop: custom validator check failed for ' +
         prop.path + '="' + prop.raw + '"'
       )

+ 3 - 8
src/util/debug.js

@@ -1,15 +1,10 @@
-var config = require('../config')
-
 /**
- * Enable debug utilities. The enableDebug() function and
- * all _.log() & _.warn() calls will be dropped in the
- * minified production build.
+ * Enable debug utilities.
  */
 
-enableDebug()
-
-function enableDebug () {
+if (process.env.NODE_ENV !== 'production') {
 
+  var config = require('../config')
   var hasConsole = typeof console !== 'undefined'
 
   /**

+ 3 - 1
src/util/dom.js

@@ -13,7 +13,9 @@ exports.query = function (el) {
     var selector = el
     el = document.querySelector(el)
     if (!el) {
-      _.warn('Cannot find element: ' + selector)
+      process.env.NODE_ENV !== 'production' && _.warn(
+        'Cannot find element: ' + selector
+      )
     }
   }
   return el

+ 5 - 4
src/util/options.js

@@ -44,7 +44,7 @@ strats.data = function (parentVal, childVal, vm) {
       return parentVal
     }
     if (typeof childVal !== 'function') {
-      _.warn(
+      process.env.NODE_ENV !== 'production' && _.warn(
         'The "data" option should be a function ' +
         'that returns a per-instance value in component ' +
         'definitions.'
@@ -89,7 +89,7 @@ strats.data = function (parentVal, childVal, vm) {
 
 strats.el = function (parentVal, childVal, vm) {
   if (!vm && childVal && typeof childVal !== 'function') {
-    _.warn(
+    process.env.NODE_ENV !== 'production' && _.warn(
       'The "el" option should be a function ' +
       'that returns a per-instance value in component ' +
       'definitions.'
@@ -131,7 +131,7 @@ strats.props = function (parentVal, childVal) {
 
 strats.paramAttributes = function () {
   /* istanbul ignore next */
-  _.warn(
+  process.env.NODE_ENV !== 'production' && _.warn(
     '"paramAttributes" option has been deprecated in 0.12. ' +
     'Use "props" instead.'
   )
@@ -218,10 +218,11 @@ function guardComponents (components) {
     var def
     for (var key in components) {
       if (_.commonTagRE.test(key)) {
-        _.warn(
+        process.env.NODE_ENV !== 'production' && _.warn(
           'Do not use built-in HTML elements as component ' +
           'name: ' + key
         )
+        continue
       }
       def = components[key]
       if (_.isPlainObject(def)) {

+ 8 - 2
src/watcher.js

@@ -86,7 +86,10 @@ p.get = function () {
   try {
     value = this.getter.call(vm, vm)
   } catch (e) {
-    if (config.warnExpressionErrors) {
+    if (
+      process.env.NODE_ENV !== 'production' &&
+      config.warnExpressionErrors
+    ) {
       _.warn(
         'Error when evaluating expression "' +
         this.expression + '". ' +
@@ -127,7 +130,10 @@ p.set = function (value) {
   try {
     this.setter.call(vm, vm, value)
   } catch (e) {
-    if (config.warnExpressionErrors) {
+    if (
+      process.env.NODE_ENV !== 'production' &&
+      config.warnExpressionErrors
+    ) {
       _.warn(
         'Error when evaluating setter "' +
         this.expression + '"', e

+ 6 - 0
test/unit/lib/util.js

@@ -15,3 +15,9 @@ scope.hasWarned = function (_, msg) {
     return arg.indexOf(msg) > -1
   }
 }
+
+scope.process = {
+  env: {
+    NODE_ENV: 'development'
+  }
+}