不懂的小兵

不懂的小兵 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

小兵一枚,不断前行,终有一日,成为大兵

个人动态

不懂的小兵 赞了回答 · 2019-07-26

解决处理文件下载失败,如何将Blob对象转换为Json?

//可以现根据blob的type来判断是否是json还是stream,如果是streamname就去下载,如果是jsonname用fileReader解析blob

var reader = new FileReader();
reader.addEventListener("loadend", function() {
    console.log(JSON.parse(reader.result));
});
reader.readAsText(blob,['utf-8']);

关注 4 回答 5

不懂的小兵 回答了问题 · 2018-03-02

解决用vue-cli见得项目,Ctrl+c之后端口依然是监听状态,用命令行关闭端口很麻烦,有什么好办法么

同样遇到,找了很久原因,没找到……只有关闭进程的办法吗

关注 2 回答 2

不懂的小兵 收藏了文章 · 2018-03-02

一字一句的搞懂vue-cli之vue webpack template配置

webpack--神一样的存在。无论写了多少次,再次相见,仍是初见。有的时候开发vue项目,对尤大的vue-cli感激涕零。但是,但是,但是。。。不是自己的东西,真的很不想折腾。所以,我们就得深入内部,cp them us。所以呢,就利用两天时间,参考了一些他人的文章,查阅了一些官方的配置,就在此先稍微记录一下。

这份配置解析是基于最新版本的vue webpack template。不过,我非常建议,先别看我的文章,自己一句一句的通读一遍。然后再来瞅瞅,毕竟,碰撞的思维才能创造新的发现。

vue webpack的配置文件还是挺多的,下面是关于此配置的基本目录结构:

config
├── dev.env.js //dev环境变量配置
├── index.js // dev和prod环境的一些基本配置
└── prod.env.js // prod环境变量配置
build
├── build.js // npm run build所执行的脚本
├── check-versions.js // 检查npm和node的版本
├── logo.png
├── utils.js // 一些工具方法,主要用于生成cssLoader和styleLoader配置
├── vue-loader.conf.js // vueloader的配置信息
├── webpack.base.conf.js // dev和prod的公共配置
├── webpack.dev.conf.js // dev环境的配置
└── webpack.prod.conf.js // prod环境的配置

下面我们就按照如下的顺序分析源码:

config/index.js -> build/utils.js -> build/vue-loader.conf.js -> build/webpack.base.conf.js -> build/webpack.dev.conf.js -> build/webpack.prod.conf.js -> build/check-versions.js -> build/build.js

config/index.js: 一些基本属性的配置(我们可以根据自己的需要来更改这些配置)

'use strict'
// 这个文件主要是对开发环境和生产环境的一个基本的配置
const path = require('path')

module.exports = {
  // 开发环境的一个基本配置
  dev: {
    // 编译输出的二级目录
    assetsSubDirectory: 'static',
    // 编译发布的根目录,可配置为资源服务器域名或者cdn域名
    assetsPublicPath: '/',
    // 需要使用proxyTable代理的接口(可以跨域)
    proxyTable: {},

    // 开发时候的访问域名。可以通过环境变量自己设置。
    host: 'localhost', // can be overwritten by process.env.HOST
    // 开发时候的端口。可以通过环境变量PORT设定。如果端口被占用了,会随机分配一个未被使用的端口
    port: 8080, 
    // 是否自动打开浏览器
    autoOpenBrowser: false,
    // 下面两个都是浏览器展示错误的方式
    //  在浏览器是否展示错误蒙层
    errorOverlay: true,
    // 是否展示错误的通知
    notifyOnErrors: true,

    // 这个是webpack-dev-servr的watchOptions的一个选项,指定webpack检查文件的方式
    // 因为webpack使用文件系统去获取文件改变的通知。在有些情况下,这个可能不起作用。例如,当使用NFC的时候,
    // vagrant也会在这方面存在很多问题,在这些情况下,使用poll选项(以轮询的方式去检查文件是否改变)可以设定为true
    // 或者具体的数值,指定文件查询的具体周期。
    poll: false, 
    // 是否使用eslint loader去检查代码
    useEslint: true,
    
    // 如果设置为true,在浏览器中,eslint的错误和警告会以蒙层的方式展现。
    showEslintErrorsInOverlay: false,

    /**
     * Source Maps
     */

    // source maps的格式
    devtool: 'eval-source-map',

    // 指定是否通过在文件名称后面添加一个查询字符串来创建source map的缓存
    cacheBusting: true,
    // 关闭css的source map
    cssSourceMap: false,
  },

  build: {
    // html文件的生成的地方
    index: path.resolve(__dirname, '../dist/index.html'),

    // 编译生成的文件的目录
    assetsRoot: path.resolve(__dirname, '../dist'),
    // 编译生成的静态文件的目录
    assetsSubDirectory: 'static',
    // 编译发布的根目录,可配置为资源服务器域名或者cdn域名
    assetsPublicPath: '/',

    /**
     * Source Maps
     */

    productionSourceMap: true,
    
    devtool: '#source-map',
    
    // 是否开启生产环境的gzip压缩
    productionGzip: false,
    // 开启gzip压缩的文件的后缀名称
    productionGzipExtensions: ['js', 'css'],

    // 如果这个选项是true的话,那么则会在build后,会在浏览器中生成一份bundler报告
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

build/utils.js: 主要用于生成css loader和style loader的一些方法

'use strict'
// 引入nodejs的path模块,用于操作路径
const path = require('path')
// 引入模板的配置文件,下面就需要去这个文件中看看有什么基本的配置
const config = require('../config')
// 提取特定文件的插件,比如把css文件提取到一个文件中去
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 加载package.json文件
const packageConfig = require('../package.json')

// 生成编译输出的二级目录
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  // path.posix是path模块跨平台的实现(不同平台的路径表示是不一样的)
  return path.posix.join(assetsSubDirectory, _path)
}

// 为不同的css预处理器提供一个统一的生成方式,也就是统一处理各种css类型的打包问题。
// 这个是为在vue文件中的style中使用的css类型
exports.cssLoaders = function (options) {
  options = options || {}

  // 打包css模块
  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 编译postcss模块
  const postcssLoader = {
    // 使用postcss-loader来打包postcss模块
    loader: 'postcss-loader',
    // 配置source map
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 创建loader加载器字符串,结合extract text插件使用
  /**
   * 
   * @param {loader的名称} loader 
   * @param {loader对应的options配置对象} loaderOptions 
   */
  function generateLoaders (loader, loaderOptions) {
    // 通过usePostCSS 来标明是否使用了postcss
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    // 如果指定了具体的loader的名称
    if (loader) {
      // 向loaders的数组中添加该loader对应的加载器
      // 一个很重要的地方就是,一个数组中的loader加载器,是从右向左执行的。
      loaders.push({
        // loader加载器的名称
        loader: loader + '-loader',
        // 对应的加载器的配置对象
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // 如果明确指定了需要提取静态文件,则使用
    // ExtractTextPlugin.extract({})来包裹我们的各种css处理器。
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        // fallback这个选项我们可以这样理解
        // webpack默认会按照loaders中的加载器从右向左调用编译各种css类型文件。如果一切顺利,在loaders中的
        // 各个加载器运行结束之后就会把css文件导入到规定的文件中去,如果不顺利,则继续使用vue-style-loader来处理
        // css文件
        fallback: 'vue-style-loader'
      })
    } else {
      // 如果没有提取行为,则最后再使用vue-style-loader处理css
      return ['vue-style-loader'].concat(loaders)
    }
  }

  return {
    // css-loader
    css: generateLoaders(),
    // postcss-loader
    postcss: generateLoaders(),
    // less-loader
    less: generateLoaders('less'),
    // sass-loader 后面的选项表明sass使用的是缩进的愈发
    sass: generateLoaders('sass', { indentedSyntax: true }),
    // scss-loader
    scss: generateLoaders('sass'),
    // stylus-loader stylus文件有两种后缀名.stylus和styl
    stylus: generateLoaders('stylus'),
    // stylus-loader
    styl: generateLoaders('stylus')
  }
}

// 使用这个函数,为那些独立的style文件创建加载器配置。
exports.styleLoaders = function (options) {
  // 保存加载器配置的变量
  const output = []
  // 获取所有css文件类型的loaders
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    // 生成对应的loader配置
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
  // node-notifier是一个跨平台的包,以类似浏览器的通知的形式展示信息。
  const notifier = require('node-notifier')

  return (severity, errors) => {
    // 只展示错误的信息
    if (severity !== 'error') return

    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    // 需要展示的错误信息的内容 
    notifier.notify({
      // 通知的标题
      title: packageConfig.name,
      // 通知的主体内容
      message: severity + ': ' + error.name,
      // 副标题
      subtitle: filename || '',
      // 通知展示的icon
      icon: path.join(__dirname, 'logo.png')
    })
  }
}

build/vue-loader.conf.js:vue-loader的一些基本配置

'use strict'
const utils = require('./utils')
const config = require('../config')
// 设置是不是生产环境
const isProduction = process.env.NODE_ENV === 'production'
// 根据不同的环境,引入不同的source map配置文件
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap

module.exports = {
  // vue文件中的css loader配置
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    // 生产环境下就会把css文件抽取到一个独立的文件中
    extract: isProduction
  }),
  // css source map文件的配置
  cssSourceMap: sourceMapEnabled,
  // css source map文件缓存控制变量
  cacheBusting: config.dev.cacheBusting,
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}

build/weback.base.conf.js:dev和prod环境下的公共配置

'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
// 生成相对于根目录的绝对路径
function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

// eslint的规则
const createLintingRule = () => ({
  // 对.js和.vue结尾的文件进行eslint检查
  test: /\.(js|vue)$/,
  // 使用eslint-loader
  loader: 'eslint-loader',
  // enforce的值可能是pre和post。其中pre有点和webpack@1中的preLoader配置含义相似。
  // post和v1中的postLoader配置含义相似。表示loader的调用时机
  // 这里表示在调用其他loader之前需要先调用这个规则进行代码风格的检查
  enforce: 'pre',
  // 需要进行eslint检查的文件的目录存在的地方
  include: [resolve('src'), resolve('test')],
  // eslint-loader配置过程中需要指定的选项
  options: {
    // 文件风格的检查的格式化程序,这里使用的是第三方的eslint-friendly-formatter
    formatter: require('eslint-friendly-formatter'),
    // 是否需要eslint输出警告信息
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

// 下面就是webpack基本的配置信息(可以立即成是开发环境和生产环境公共的配置)
module.exports = {
  // webpack解析文件时候的根目录(如果把webpack.config.js)放在了项目的根目录下面,这个配置可以省略
  context: path.resolve(__dirname, '../'),
  // 指定项目的入口文件
  entry: {
    app: './src/main.js'
  },
  // 项目的输出配置
  output: {
    // 项目build的时候,生成的文件的存放路径(这里的路径是../dist)
    path: config.build.assetsRoot,
    // 生成文件的名称
    filename: '[name].js',
    // 输出解析文件的目录,url 相对于 HTML 页面(生成的html文件中,css和js等静态文件的url前缀)
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  // 配置模块解析时候的一些选项
  resolve: {
    // 指定哪些类型的文件可以引用的时候省略后缀名
    extensions: ['.js', '.vue', '.json'],
    // 别名,在引入文件的时候可以使用
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      // 可以在引入文件的时候使用@符号引入src文件夹中的文件
      '@': resolve('src'),
    }
  },
  // 下面是针对具体的模块进行的具体的配置
  // 下面的配置语法采用的是version >= @2的版本
  module: {
    // rules是一个数组,其中的每一个元素都是一个对象,这个对象是针对具体类型的文件进行的配置。
    rules: [
      // .vue文件的配置
      {
        // 这个属性是一个正则表达式,用于匹配文件。这里匹配的是.vue文件
        test: /\.vue$/,
        // 指定该种类型文件的加载器名称
        loader: 'vue-loader',
        // 针对此加载器的具体配置
        // 针对前面的分析,这个配置对象中包含了各种css类型文件的配置,css source map的配置 以及一些transform的配置
        options: vueLoaderConfig
      },
      {
        // .js文件的配置
        test: /\.js$/,
        // js文件的处理主要使用的是babel-loader。在这里没有指定具体的编译规则,babel-loader会自动
        // 读取根目录下面的.babelrc中的babel配置用于编译js文件
        /**
         * {
         * // 使用的预设
            "presets": [
              // babel-preset-env: 根据你所支持的环境自动决定具体类型的babel插件
              ["env", {
                // modules设置为false,不会转换module
                "modules": false
              }],
              // babel-preset-stage-2: 可以使用所有>=stage2语法
              "stage-2"
            ],
            // 使用的插件
            // babel-plugin-transform-runtime: 只会对es6的语法进行转换而不会对新的api进行转换
            // 如果需要支持新的api,请引入babel-polyfill
            "plugins": ["transform-runtime"]
          }

         */
        loader: 'babel-loader',
        // 指定需要进行编译的文件的路径
        // 这里表示只对src和test文件夹中的文件进行编译
        include: [resolve('src'), resolve('test')]
      },
      {
        // 对图片资源进行编译的配置
        // 指定文件的类型
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        // 使用url-loader进行文件资源的编译
        loader: 'url-loader',
        // url-loader的配置选项
        options: {
          // 文件的大小小于10000字节(10kb)的时候会返回一个dataUrl
          limit: 10000,
          // 生成的文件的保存路径和后缀名称
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        // 对视频文件进行打包编译
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        // 对字体文件进行打包编译
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  // 这些选项用于配置polyfill或mock某些node.js全局变量和模块。
  // 这可以使最初为nodejs编写的代码可以在浏览器端运行
  node: {
    // 这个配置是一个对象,其中的每个属性都是nodejs全局变量或模块的名称
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    // false表示什么都不提供。如果获取此对象的代码,可能会因为获取不到此对象而触发ReferenceError错误
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    // 设置成empty则表示提供一个空对象
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

build/weboack.dev.conf.js:dev环境的配置

'use strict'
// 首先引入的是一些工具方法,下面我们就需要去util文件种看一下有哪些对应的工具方法
const utils = require('./utils')
// 引入webpack模块
const webpack = require('webpack')
// 引入配置文件
// 这个配置文件中包含了一些dev和production环境的基本配置
const config = require('../config')
// 引入webpack-merge模块。这个模块用于把多个webpack配置合并成一个配置,后面的配置会覆盖前面的配置。
const merge = require('webpack-merge')
// 引入webpack的基本设置,这个设置文件包含了开发环境和生产环境的一些公共配置
const baseWebpackConfig = require('./webpack.base.conf')
// 用于生成html文件的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 这个插件能够更好的在终端看到webpack运行时的错误和警告等信息。可以提升开发体验。
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查找一个未使用的端口
const portfinder = require('portfinder')

// 获取host环境变量,用于配置开发环境域名
const HOST = process.env.HOST
// 获取post环境变量,用于配置开发环境时候的端口号
const PORT = process.env.PORT && Number(process.env.PORT)

// 开发环境的完整的配置文件,
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // 为那些独立的css类型文件添加loader配置(没有写在vue文件的style标签中的样式)
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // 开发环境使用'eval-source-map'模式的source map
  // 因为速度快
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  // 下面是对webpack-dev-server选项的基本配置,这些配置信息,我们可以在/config/index.js
  // 文件中进行自定义配置。
  devServer: {
    // 用于配置在开发工具的控制台中显示的日志级别
    // 注意这个不是对bundle的错误和警告的配置,而是对它生成之前的消息的配置
    clientLogLevel: 'warning',
    // 表示当使用html5的history api的时候,任意的404响应都需要被替代为index.html
    historyApiFallback: true,
    // 启用webpack的热替换特性
    hot: true,
    // 一切服务都需要使用gzip压缩
    // 可以在js,css等文件的response header中发现有Content-Encoding:gzip响应头
    compress: true,
    // 指定使用一个 host。默认是 localhost
    // 如果希望服务器外部可以访问(通过我们电脑的ip地址和端口号访问我们的应用)
    // 可以指定0.0.0.0
    host: HOST || config.dev.host,
    // 指定要监听请求的端口号
    port: PORT || config.dev.port,
    // 是否自动打开浏览器
    open: config.dev.autoOpenBrowser,
    // 当编译出现错误的时候,是否希望在浏览器中展示一个全屏的蒙层来展示错误信息
    overlay: config.dev.errorOverlay
    // 表示只显示错误信息而不显示警告信息
    // 如果两者都希望显示,则把这两项都设置为true
      ? { warnings: false, errors: true }
      // 设置为false则表示啥都不显示
      : false,
      // 指定webpack-dev-server的根目录,这个目录下的所有的文件都是能直接通过浏览器访问的
      // 推荐和output.publicPath设置为一致
    publicPath: config.dev.assetsPublicPath,
    // 配置代理,这样我们就可以跨域访问某些接口
    // 我们访问的接口,如果符合这个选项的配置,就会通过代理服务器转发我们的请求
    proxy: config.dev.proxyTable,
    // 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
    quiet: true, // necessary for FriendlyErrorsPlugin
    // 与监视文件相关的控制选项。
    watchOptions: {
      // 如果这个选项为true,会以轮询的方式检查我们的文件的变动,效率不好
      poll: config.dev.poll,
    }
  },
  plugins: [
    // 创建一个在编译时可以配置的全局变量
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    // 启用热替换模块
    // 记住,我们永远不要再生产环境中使用hmr
    new webpack.HotModuleReplacementPlugin(),
    // 这个插件的主要作用就是在热加载的时候直接返回更新文件的名称,而不是文件的id
    new webpack.NamedModulesPlugin(),
    // 使用这个插件可以在编译出错的时候来跳过输出阶段,这样可以确保输出资源不会包含错误。
    new webpack.NoEmitOnErrorsPlugin(),

    // 这个插件主要是生成一个html文件
    new HtmlWebpackPlugin({
      // 生成的html文件的名称
      filename: 'index.html',
      // 使用的模板的名称
      template: 'index.html',
      // 将所有的静态文件都插入到body文件的末尾
      inject: true
    }),
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  // 这种获取port的方式会返回一个promise
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // 把获取到的端口号设置为环境变量PORT的值
      process.env.PORT = port
      // 重新设置webpack-dev-server的端口的值
      devWebpackConfig.devServer.port = port

      // 将FriendlyErrorsPlugin添加到webpack的配置文件中
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 编译成功时候的输出信息
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        // 当编译出错的时候,根据config.dev.notifyOnErrors来确定是否需要在桌面右上角显示错误通知框
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))
      // resolve我们的配置文件
      resolve(devWebpackConfig)
    }
  })
})

build/webpack.prod.conf.js:prod环境的基本配置

'use strict'
// 引入path模块
const path = require('path')
// 引入工具方法
const utils = require('./utils')
// 引入webpack模块
const webpack = require('webpack')
// 引入基本的配置
const config = require('../config')
// 引入webpack-merge模块
const merge = require('webpack-merge')
// 引入开发环境和生产环境公共的配置
const baseWebpackConfig = require('./webpack.base.conf')
// 引入copy-webpack-plugin模块
// 这个模块主要用于在webpack中拷贝文件和文件夹
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 引入html-webpack-plugin插件
// 这个插件主要是用于基于模版生成html文件的
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入extract-text-webpack-plugin插件
// 这个插件主要是用于将入口中所有的chunk,移到独立的分离的css文件中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 引入optimize-css-assets-webpack-plugin插件
// 这个插件主要是用于压缩css模块的
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 引入uglifyjs-webpack-plugin插件
// 这个插件主要是用于压缩js文件的
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// 引入用于生产环境的一些基本变量
const env = require('../config/prod.env')

// 合并公共配置和生产环境独有的配置并返回一个用于生产环境的webpack配置文件
const webpackConfig = merge(baseWebpackConfig, {
  // 用于生产环境的一些loader配置
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      // 在生产环境中使用extract选项,这样就会把thunk中的css代码抽离到一份独立的css文件中去
      extract: true,
      usePostCSS: true
    })
  },
  // 配置生产环境中使用的source map的形式。在这里,生产环境使用的是#source map的形式
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    // build所产生的文件的存放的文件夹地址
    path: config.build.assetsRoot,
    // build之后的文件的名称
    // 这里[name]和[chunkhash]都是占位符
    // 其中[name]指的就是模块的名称
    // [chunkhash]chunk内容的hash字符串,长度为20
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // [id]也是一个占位符,表示的是模块标识符(module identifier)
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 压缩javascript的插件
    new UglifyJsPlugin({
      // 压缩js的时候的一些基本配置
      uglifyOptions: {
        // 配置压缩的行为
        compress: {
          // 在删除未使用的变量等时,显示警告信息,默认就是false
          warnings: false
        }
      },
      // 使用 source map 将错误信息的位置映射到模块(这会减慢编译的速度)
      // 而且这里不能使用cheap-source-map
      sourceMap: config.build.productionSourceMap,
      // 使用多进程并行运行和文件缓存来提高构建速度
      parallel: true
    }),

    // 提取css文件到一个独立的文件中去
    new ExtractTextPlugin({
      // 提取之后css文件存放的地方
      // 其中[name]和[contenthash]都是占位符
      // [name]就是指模块的名称
      // [contenthash]根据提取文件的内容生成的 hash
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      
      // 从所有额外的 chunk(additional chunk) 提取css内容
      // (默认情况下,它仅从初始chunk(initial chunk) 中提取)
      // 当使用 CommonsChunkPlugin 并且在公共 chunk 中有提取的 chunk(来自ExtractTextPlugin.extract)时
      // 这个选项需要设置为true
      allChunks: false,
    }),
    // duplicated CSS from different components can be deduped.
    // 使用这个插件压缩css,主要是因为,对于不同组件中相同的css可以剔除一部分
    new OptimizeCSSPlugin({
      // 这个选项的所有配置都会传递给cssProcessor
      // cssProcessor使用这些选项决定压缩的行为
      cssProcessorOptions: config.build.productionSourceMap
      // safe我不是很明白是什么意思???求留言告知。。。
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    
    // 创建一个html文件
    new HtmlWebpackPlugin({
      // 生成的文件的名称
      filename: config.build.index,
      // 使用的模板的名称
      template: 'index.html',
      // 把script和link标签放在body底部
      inject: true,
      // 配置html的压缩行为
      minify: {
        // 移除注释
        removeComments: true,
        // 去除空格和换行
        collapseWhitespace: true,
        // 尽可能移除属性中的引号和空属性
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // 控制chunks的顺序,这里表示按照依赖关系进行排序
      // 也可以是一个函数,自己定义排序规则
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vender modules does not change
    // 根据模块的相对路径生成一个四位数的hash作为模块id
    new webpack.HashedModuleIdsPlugin(),

    // webpack2处理过的每一个模块都会使用一个函数进行包裹
    // 这样会带来一个问题:降低浏览器中JS执行效率,这主要是闭包函数降低了JS引擎解析速度。
    // webpack3中,通过下面这个插件就能够将一些有联系的模块,
    // 放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS的执行速度。
    new webpack.optimize.ModuleConcatenationPlugin(),

    // 这个插件用于提取多入口chunk的公共模块
    // 通过将公共模块提取出来之后,最终合成的文件能够在最开始的时候加载一次
    // 然后缓存起来供后续使用,这会带来速度上的提升。
    new webpack.optimize.CommonsChunkPlugin({
      // 这是 common chunk 的名称
      name: 'vendor',
      // 把所有从mnode_modules中引入的文件提取到vendor中
      minChunks (module) {
      
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),

    // 为了将项目中的第三方依赖代码抽离出来,官方文档上推荐使用这个插件,当我们在项目里实际使用之后,
    // 发现一旦更改了 app.js 内的代码,vendor.js 的 hash 也会改变,那么下次上线时,
    // 用户仍然需要重新下载 vendor.js 与 app.js——这样就失去了缓存的意义了。所以第二次new就是解决这个问题的
    // 参考:https://github.com/DDFE/DDFE-blog/issues/10
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    // 拷贝静态资源到build文件夹中
    new CopyWebpackPlugin([
      {
        // 定义要拷贝的资源的源目录
        from: path.resolve(__dirname, '../static'),
        // 定义要拷贝的资源的目标目录
        to: config.build.assetsSubDirectory,
        // 忽略拷贝指定的文件,可以使用模糊匹配
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  // 如果开启了生产环境的gzip
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      // 目标资源的名称
      // [path]会被替换成原资源路径
      // [query]会被替换成原查询字符串
      asset: '[path].gz[query]',
      // gzip算法
      // 这个选项可以配置成zlib模块中的各个算法
      // 也可以是(buffer, cb) => cb(buffer)
      algorithm: 'gzip',
      // 处理所有匹配此正则表达式的资源
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      // 只处理比这个值大的资源
      threshold: 10240,
      // 只有压缩率比这个值小的资源才会被处理
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  // 如果需要生成一分bundle报告,则需要使用下面的这个插件
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

build/check-versions.js:检查npm和node的版本

'use strict'
// 在终端为不同字体显示不同的风格
const chalk = require('chalk')
// 解析npm包的version
const semver = require('semver')
// 引入package.json文件
const packageConfig = require('../package.json')
// node版本的uninx shell命令
const shell = require('shelljs')

// 执行命令的函数
function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node',
    // node的版本
    // process.version就是node的版本
    // semver.clean('v8.8.0') => 8.8.0
    currentVersion: semver.clean(process.version),
    // package.json中定义的node版本的范围 
    versionRequirement: packageConfig.engines.node
  }
]

// 相当于 which npm
if (shell.which('npm')) {
  // 如果npm命令存在的话
  versionRequirements.push({
    name: 'npm',
    // 检查npm的版本 => 5.4.2
    currentVersion: exec('npm --version'),
    // package.json中定义的npm版本
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  const warnings = []

  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i]

    // semver.satisfies()进行版本之间的比较
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      // 如果现有的npm或者node的版本比定义的版本低,则生成一段警告
      warnings.push(mod.name + ': ' +
        chalk.red(mod.currentVersion) + ' should be ' +
        chalk.green(mod.versionRequirement)
      )
    }
  }

  if (warnings.length) {
    console.log('')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()

    for (let i = 0; i < warnings.length; i++) {
      const warning = warnings[i]
      console.log('  ' + warning)
    }

    console.log()
    // 退出程序
    process.exit(1)
  }
}

build/build.js: build项目

'use strict'
// 检查npm和node的版本
require('./check-versions')()

// 设置环境变量NODE_ENV的值是production
process.env.NODE_ENV = 'production'

// 终端的spinner
const ora = require('ora')
// node.js版本的rm -rf
const rm = require('rimraf')
// 引入path模块
const path = require('path')
// 引入显示终端颜色模块
const chalk = require('chalk')
// 引入webpack模块
const webpack = require('webpack')
// 引入基本的配置文件
const config = require('../config')
// 引入webpack在production环境下的配置文件
const webpackConfig = require('./webpack.prod.conf')

// 
const spinner = ora('building for production...')
spinner.start()

// 删除打包目标目录下的文件
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  // 进行打包
  webpack(webpackConfig, (err, stats) => {
    // 打包完成
    spinner.stop()
    if (err) throw err
    // 输出打包的状态
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    // 如果打包出现错误
    if (stats.hasErrors()) {
      console.log(chalk.red('  Build failed with errors.\n'))
      process.exit(1)
    }

    // 打包完成
    console.log(chalk.cyan('  Build complete.\n'))
    console.log(chalk.yellow(
      '  Tip: built files are meant to be served over an HTTP server.\n' +
      '  Opening index.html over file:// won\'t work.\n'
    ))
  })
})

拍砖,bingo?

查看原文

不懂的小兵 收藏了文章 · 2018-03-01

vue2+element 管理后台 集成解决方案 没有没做的,只要想不到的!

完整项目地址:vue-element-admin
系类文章一:手摸手,带你用vue撸后台 系列一(基础篇)
系类文章二:手摸手,带你用vue撸后台 系列二(登录权限篇)
系类文章三:手摸手,带你用vue撸后台 系列三(实战篇)
系类文章四:手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)

前言

这半年来一直在用vue写管理后台,目前后台已经有七十多个页面,十几种权限,但维护成本依然很低,效率依然很高,所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios.由于是个人项目,所以数据请求都是用了mockjs代替。

后续会出一系列的教程配套文章,如如何从零构建后台项目框架,如何做完整的用户系统(如权限验证,二次登录等),如何二次开发组件(如富文本),如何整合七牛等等文章,各种后台开发经验等等。莫急~~

功能

  • 登录/注销
  • 权限验证
  • 侧边栏
  • 面包屑
  • 富文本编辑器
  • Markdown编辑器
  • JSON编辑器
  • 列表拖拽
  • plitPane
  • Dropzone
  • Sticky
  • CountTo
  • echarts图表
  • 401,401错误页面
  • 错误日志
  • 导出excel
  • table example
  • form example
  • 多环境发布
  • dashboard
  • 二次登录
  • 动态侧边栏
  • mock数据
  • svg iconfont

开发

    # 克隆项目
    git clone https://github.com/PanJiaChen/vue-element-admin.git

    # 安装依赖
    npm install

    # 本地开发 开启服务
    npm run dev

浏览器访问 http://localhost:9527

发布

    # 发布测试环境 带webpack ananalyzer
    npm run build:sit-preview

    # 构建生成环境
    npm run build:prod

目录结构

├── build                      // 构建相关  
├── config                     // 配置相关
├── src                        // 源代码
│   ├── api                    // 所以请求
│   ├── assets                 // 主题 字体等静态资源
│   ├── components             // 全局公用组件
│   ├── directive              // 全局指令
│   ├── filtres                // 全局filter
│   ├── mock                   // mock数据
│   ├── router                 // 路由
│   ├── store                  // 全局store管理
│   ├── styles                 // 全局样式
│   ├── utils                  // 全局公用方法
│   ├── view                   // view
│   ├── App.vue                // 入口页面
│   └── main.js                // 入口 加载组件 初始化等
├── static                     // 第三方不打包资源
│   ├── jquery
│   └── Tinymce                // 富文本
├── .babelrc                   // babel-loader 配置
├── eslintrc.js                // eslint 配置项
├── .gitignore                 // git 忽略项
├── favicon.ico                // favicon图标
├── index.html                 // html模板
└── package.json               // package.json

状态管理

后台只有user和app配置相关状态使用vuex存在全局,其它数据都由每个业务页面自己管理。

效果图

两步验证登录 支持微信和qq

2login.gif)

真正的动态换肤

图片描述

拖拽排序

clipboard.png

上传裁剪头像

clipboard.png

错误统计

clipboard.png

富文本(整合七牛 打水印等个性化功能)

clipboard.png

更多demo

占坑

系类文章一

查看原文

不懂的小兵 收藏了文章 · 2018-03-01

Vue项目SSR改造实战

博客已全站升级到https,如果遇到无法访问,请手动加上https://前缀

我们先看“疗效”,你可以打开我的博客u3xyz.com,通过查看源代码来看SSR直出效果。我的博客已经快上线一年了,但不吹不黑,访问量非常地小,我也一直在想办法提升访问量(包括在sf写文章,哈哈)。当然,在PC端,搜索引擎一直都是一个重要的流量来源。这里就不得不提到SEO。下图是我的博客以前在百度的快照:

SSR前快照

细心的朋友会发现,这个快照非常简单,简单到几乎什么都没有。这也是没办法的事,博客是基于Vue的SPA页面,整个项目本来就是一个“空架子”,这个快照从博客2月份上线以来就一直是上面的样子,直到最近上线SSR。搜索引擎蜘蛛每次来抓取你的网站都是一个样子,慢慢得,它也就不会来了,相应的,网站的权重,排名肯定不会好。到目前为此,我的博客不用网址进行搜索都搜不到。在上线了SSR后,再加上一些SEO优化,百度快照终于更新了:

SSR后快照

为什么要做SSR

文章开始基本已经回答了为什么要做SSR这个问题,当然,还有另一个原因是SSR概念现在在前端非常火,无奈在实际项目中没有机会,也只有拿博客来练手了。下面将详细介绍本博客项目SSR全过程。

SSR改造实战

总的来说SSR改造还是相当容易的。推荐在动手之前,先了解官方文档官方Vue SSR Demo,这会让我们事半功倍。

1. 构建改造

VueSSR原理

上图是Vue官方的SSR原理介绍图片。从这张图片,我们可以知道:我们需要通过Webpack打包生成两份bundle文件:

  • Client Bundle,给浏览器用。和纯Vue前端项目Bundle类似
  • Server Bundle,供服务端SSR使用,一个json文件

不管你项目先前是什么样子,是否是使用vue-cli生成的。都会有这个构建改造过程。在构建改造这里会用到 vue-server-renderer 库,这里要注意的是 vue-server-renderer 版本要与Vue版本一样。下图是我的构建文件目录:

构建

  • util.js 提供一些公共方法
  • webpack.base.js是公共的配置
  • webpack.client.js 是生成Client Bundle的配置。核心配置如下:
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

// ...

const config = merge(baseConfig, {
  target: 'web',
  entry: './src/entry.client.js',
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"client"'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vender',
      minChunks: 2
    }),
    // extract webpack runtime & manifest to avoid vendor chunk hash changing
    // on every build.
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest'
    }),
    new VueSSRClientPlugin()
  ]
})
  • webpack.server.js 是生成Server Bundle的配置,核心配置如下:
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

// ...

const config = merge(baseConfig, {
  target: 'node',
  devtool: '#source-map',
  entry: './src/entry.server.js',
  output: {
    libraryTarget: 'commonjs2',
    filename: 'server-bundle.js'
  },
  externals: nodeExternals({
    // do not externalize CSS files in case we need to import it from a dep
    whitelist: /\.css$/
  }),
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
      'process.env.VUE_ENV': '"server"'
    }),
    new VueSSRServerPlugin()
  ]
})

2. 代码改造

2.1 必须使用VueRouter, Vuex。ajax库建议使用axios

可能你的项目没有使用VueRouter或Vuex。但遗憾的是,Vue-SSR必须基于 Vue + VueRouter + Vuex。Vuex官方没有提,但其实文档和Demo都是基于Vuex。我的博客以前也没有用Vuex,但经过一翻折腾后,还是乖乖加上了Vuex。另外,因为代码要能同时在浏览器和Node.js环境中运行,所以ajax库建议使用axios这样的跨平台库。

2.2 两个打包入口(entry),重构app, store, router, 为每个对象增加工厂方法createXXX

每个用户通过浏览器访问Vue页面时,都是一个全新的上下文,但在服务端,应用启动后就一直运行着,处理每个用户请求的都是在同一个应用上下文中。为了不串数据,需要为每次SSR请求,创建全新的app, store, router

项目目录

上图是我的项目文件目录。

  • app.js, 通用的启动Vue应用代码
  • App.vue,Vue应用根组件
  • entry.client.js,浏览器环境入口
  • entry.server.js,服务器环境入口
  • index.html,html模板

再看一下具体实现的核心代码:

// app.js

import Vue from 'vue'
import App from './App.vue' // 根组件
import {createRouter} from './routers/index' 
import {createStore} from './vuex/store'
import {sync} from 'vuex-router-sync' // 把当VueRouter状态同步到Vuex中

// createApp工厂方法
export function createApp (ssrContext) {
  let router = createRouter() // 创建全新router实例
  let store = createStore() // 创建全新store实例

  // 同步路由状态到store中
  sync(store, router)
  
  // 创建Vue应用
  const app = new Vue({
    router,
    store,
    ssrContext,
    render: h => h(App)
  })
  return {app, router, store}
}
// entry.client.js 

import Vue from 'vue'
import { createApp } from './app'

const { app, router, store } = createApp()

// 如果有__INITIAL_STATE__变量,则将store的状态用它替换
if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
}

router.onReady(() => {
    
  // 通过路由勾子,执行拉取数据逻辑
  router.beforeResolve((to, from, next) => {
    // 找到增量组件,拉取数据 
    const matched = router.getMatchedComponents(to) 
    const prevMatched = router.getMatchedComponents(from) 
    let diffed = false
    const activated = matched.filter((c, i) => {
      return diffed || (diffed = (prevMatched[i] !== c))
    })
    // 组件数据通过执行asyncData方法获取
    const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _)
    if (!asyncDataHooks.length) {
      return next()
    }
    // 要注意asyncData方法要返回promise,asyncData调用的vuex action也必须返回promise
    Promise.all(asyncDataHooks.map(hook => hook({ store, route: to })))
      .then(() => {
        next()
      })
      .catch(next)
  })

  // 将Vue实例挂载到dom中,完成浏览器端应用启动
  app.$mount('#app')
})
// entry.server.js
import { createApp } from './app'

export default context => {
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp(context)

    // 设置路由
    router.push(context.url)

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }

      // 执行asyncData方法,预拉取数据
      Promise.all(matchedComponents.map(Component => {
        if (Component.asyncData) {
          return Component.asyncData({
            store: store,
            route: router.currentRoute
          })
        }
      })).then(() => {
        // 将store的快照挂到ssr上下文上
        context.state = store.state
        resolve(app)
      }).catch(reject)
    }, reject)
  })
}
// createStore

import Vue from 'vue'
import Vuex from 'vuex'
// ...

Vue.use(Vuex)

// createStore工厂方法
export function createStore () {
  return new Vuex.Store({
    // rootstate
    state: {
      appName: 'appName',
      title: 'home'
    },

    modules: {
      // ...
    },

    strict: process.env.NODE_ENV !== 'production' // 线上环境关闭store检查
  })
}
// createRouter

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

// createRouter工厂方法
export function createRouter () {
  return new Router({
    mode: 'history', // 注意这里要使用history模式,因为hash不会发送到服务端
    fallback: false,
    routes: [
      {
        path: '/index',
        name: 'index',
        component: () => System.import('./index/index.vue') // 代码分片
      },
      {
        path: '/detail/:aid',
        name: 'detail',
        component: () => System.import('./detail/detail.vue')
      },
      // ...
      {
        path: '/',
        redirect: '/index'
      }
    ]
  })
}

3. 重构组件获取数据方式

关于状态管理,要严格遵守Redux思想。建议把应用所有状态都存于store中,组件使用时再mapState下来,状态更改严格使用action的方式。另一个要提一点的是,action要返回promise。这样我们就可以使用asyncData方法获取组件数据了

const actions = {
  getArticleList ({state, commit}, curPageNum) {
    commit(FETCH_ARTICLE_LIST, curPageNum)

    // action 要返回promise
    return apis.getArticleList({
      data: {
        size: state.pagi.itemsPerPage,
        page: curPageNum
      }
    }).then((res) => {
      // ...
    })
  }
}

// 组件asyncData实现
export default {
  asyncData ({ store }) {
    return store.dispatch('getArticleList', 1)
  }
}

3. SSR服务器实现

在完成构建和代码改造后,如果一切顺利。我们能得到下面的打包文件:

dist文件

这时,我们可以开始实现SSR服务端代码了。下面是我博客SSR实现(基于Koa)

// server.js
const Koa = require('koa')
const path = require('path')
const logger = require('./logger')
const server = new Koa()
const { createBundleRenderer } = require('vue-server-renderer')
const templateHtml = require('fs').readFileSync(path.resolve(__dirname, './index.template.html'), 'utf-8')

let distPath = './dist'

const renderer = createBundleRenderer(require(`${distPath}/vue-ssr-server-bundle.json`), { 
  runInNewContext: false,
  template: templateHtml, 
  clientManifest: require(`${distPath}/vue-ssr-client-manifest.json`) 
})

server.use(function * (next) {
  let ctx = this
  const context = { url: ctx.req.url, pageTitle: 'default-title' }

  // cgi请求,前端资源请求不能转到这里来。这里可以通过nginx做
  if (/\.\w+$/.test(context.url)) {
    return yield next
  }

  // 注意这里也必须返回promise  
  return new Promise((resolve, reject) => {
    renderer.renderToString(context, function (err, html) {
      if (err) {
        logger.error(`[error][ssr-error]: ` + err.stack)
        return reject(err)
      }
      ctx.status = 200
      ctx.type = 'text/html; charset=utf-8'
      ctx.body = html
      resolve(html)
    })
  })
})

// 错误处理
server.on('error', function (err) {
  logger.error('[error][server-error]: ' + err.stack)
})

let port = 80

server.listen(port, () => {
  logger.info(`[info]: server is deploy on port: ${port}`)
})

4. 服务器部署

服务器部署,跟你的项目架构有关。比如我的博客项目在服务端有2个后端服务,一个数据库服务,nginx用于请求转发:

u3xyz架构

5. 遇到的问题及解决办法

加载不到组件的JS文件
[vue-router] Failed to resolve async component default: Error: Cannot find module 'js\main1.js'
[vue-router] uncaught error during route navigation:

解决办法:

去掉webpack配置中的output.chunkFilename: getFileName('js/main[name]-$hash.js')

if you are using CommonsChunkPlugin, make sure to use it only in the client config because the server bundle requires a single entry chunk.

所以对webpack.server.js不要对配置CommonsChunkPlugin,也不要设置output.chunkFilename

代码高亮codeMirror使用到navigator对象,只能在浏览器环境运行

把执行逻辑放到mounted回调中。实现不行,就封装一个异步组件,把组件的初始化放到mounted中:

mounted () {
  let paragraph = require('./paragraph.vue')
  Vue.component('paragraph', paragraph)
  new Vue().$mount('#paragraph')
},
串数据

dispatch的action没有返回promise,保证返回promise即可

路由跳转

路由跳转使用router方法或<router-link />标签,这两种方式能自适应浏览器端和服务端,不要使用a标签

小结

本文主要记录了我的博客u3xyz.comSSR过程:

  • 构建webpack改造
  • 代码改造
  • server端SSR实现
  • 上线部署

最后希望文章能对大家有些许帮助!

愿文地址:Vue项目SSR改造实战

查看原文

不懂的小兵 收藏了文章 · 2018-03-01

从零开始搭建vue-ssr系列之一:写在前面的话

为什么要用vue-ssr?
  • 前端用的是vue, 后端渲染用vue-ssr,可以无缝的和前端连接起来
  • 使用vue-ssr可以把数据渲染成HTML, 并在首屏展示, 用户体验好, 传统的前端vue, 服务器第一次请求只返回#app的空DOM, 当js和ajax请求完成, 才会展示, 体验差
  • 利于SEO
是所有情况都适用vue-ssr吗?
  • 当然不是, 他的最最主要作用是首屏渲染, 其他都是次要的, 比如有3个tab页签, 只有第一个页签是首屏展示的, 其他两个是通过点击才展示数据, 那这样就没有必要把另外两个页签的数据也取出来, 做vue-ssr, 这样会增加服务器端的压力和流量, 这个后面会说到
vue-ssr很容易上手吗?
  • 说实话, 不是很容易, 虽然现在网上的例子很多, 官方也有一个例子vue-hackernews, 但是官方给出的例子太复杂, 属于大而全的例子, 不太适合原理不太清楚的新手, 网上的例子一般都是半个流程, 比如只告诉你渲染简单的模板, 根本不会把项目中用到的整个流程都串起来, 而且机理性的东西的文章也少,增加了学习的难度。
说了这么多,你写的东西能干啥?
  • 这个系列文章是从头开始搭建整个项目的,从一个实际的一个简单的场景,告诉你怎么样用client端渲染,怎么在server端取数据,并传递给前端,达到数据共享,以及告诉你用ssr时踩的一些坑,如何解决的。我搞这个也搞了一段时间,网上资料也查了好多,我也算是集各家之所长吧,尽量写的详细些,帮忙各位能从0开始搭建起来。
技术栈是什么?
  • vue2+webpack2+vuex+axios
Vue-SSR系列目录

从零开始搭建vue-ssr系列之一:写在前面的话

从零开始搭建vue-ssr系列之二:纯client端渲染以及webpack2+vue2注意事项

从零开始搭建vue-ssr系列之三:服务器渲染的奥秘

从零开始搭建vue-ssr系列之四:Vuex详解

从零开始搭建vue-ssr系列之五:开始第一个简单的server-render

从零开始搭建vue-ssr系列之六:一个完整的项目

查看原文

不懂的小兵 赞了文章 · 2015-06-02

github上值得关注的前端项目

http://microjs.com/#

该网站的资源都托管到了githubmicrojs.com是一个可以让你选择微型的js类库的网站,该网站里的js库都是压缩后不大于5KB的,非常实用

图片描述

https://plainjs.com/(10.22更新)

The Vanilla JavaScript Repository,该仓库都是用原生js写的插件和组件,很实用。里面的项目也都托管到了github

图片描述

综合/资源


  • front-end-collect 分享自己长期关注的前端开发相关的优秀网站、博客、以及活跃开发者。star:860



  • f2e-hub 包含Animation,UI,dialog,Carousels,color,image,workflow等。star:100



  • fks 前端技能汇总,包含前端知识架构,后端知识,linux,书籍推荐等。star:4000


  • node123node.js中文资料导航。star:1200



    • Front-end-tutorial 最全的资源教程-前端涉及的所有知识体系。(12.25更新)

    样式/UI/css

    • Semantic-UI 让你使用任何HTML标签 来表现UI控件。

    这是一款语义化设计的前端框架,为攻城师而制作的可复用的开源前端框架。star:17500

    图片描述

    • primerCSS风格指南。star:3600


    • glue 一个生成CSS sprites的简单的命令行工具。star:2.5K (7.19更新)


    • postcss 用js插件来对css进行转换,类似Sass的预编译器,但实现了模块化,并且更加强大。star:4.5K(7.31更新)



    • mui 轻量级css框架。star:1.5K(10.15更新)
    • img2css 将图片转为纯css的黑科技。star:1.5K(12.25更新)

    测试/工具

    • mocha 一个简单、灵活有趣的 JavaScript 测试框架,用于 Node.js 和浏览器上的 JavaScript 应用测试。 star:6680

    687474703a2f2f662e636c2e6c792f6974656d732f336c316b306e32413155334d3149314c323130702f53637265656e25323053686f74253230323031322d30322d32342532306174253230322e32312e3433253230504d2e706e67

    • csscss css代码冗余分析仪,用于分析冗余 。star:2800


    • es6-toolses6 工具集,包括Grunt Tasks,Gulp Plugins,Broccoli Plugins,Brunch Plugins,Webpack plugins等等。star:1860


    • async 一个工具模块,提供了直接而强大的 JavaScript 异步功能。虽然是为 Node.js 设计的,但是它也可以直接在浏览器中使用。star:13000


    • simditor 团队协作工具 Tower 使用的富文本编辑器。star:1300


    • HTMLHintHTML 静态代码分析工具,可以集成到IDE环境或编译系统中。star:900


    • jshintjs静态代码分析工具,可以帮你检测js语法错误和潜在的问题。star:5100


    • csslint 分析和优化你的CSS样式表的工具。由[Nicholas C.
      Zakas][30]所写。star:2700


    • protractor 一款端对端的angular apps 测试框架。star:4K


    • casperjs 一个基于PhantomJS的开源导航脚本和测试工具。star:4.8K


    • Karma 自动化完成单元测试,允许你在多个浏览器里执行js代码。让你的TDD变得简单,快速,有趣。star:5.3K


    • jasmine 是一个简易的JS单元测试框架, 用来测试Javascript代码。star:9.1K(6.28更新)


    • chai 一个针对 Node.js 和浏览器的TDD(测试驱动开发)/BDD(行为驱动开发)的断言框架,可与任何 JavaScript 测试框架集成。star:2K(6.29更新)


    • Qunit 一个很容易使用的js单元测试框架,该框架是由jQuery团队的成员所开发,并且是jQuery的官方测试套件。star:3.3K(6.29更新)

    Workflow/构建工具

    • Grunt 基于Node.js的项目构建工具。拥有数量庞大的插件,是一款优秀的前端自动化工具。star:9500


    • yeoman 一个强健的工具,库,及工作流程的组合。star:960


    • gulp 基于node.js流的新一代前端构建系统。star:14000


    • spmCMD 的包管理工具,需要和 Sea.js 配合使用。

    canvas/数据可视化

    • echarts 基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。star:6900


    • Chart.js 使用<canvas>标签的简易HTML5图表。star:14600


    • sketch.js 跨平台JavaScript创意编码框架,gzip压缩后仅有2kb。star:1500


    • d3 一个基于数据操作文档的js数据可视化框架,最流行的可视化库之一。star:38000


    • zrender 一个轻量级的Canvas类库,MVC封装,数据驱动,提供类DOM事件模型,让canvas绘图大不同!star:850


    • c3 一个基于 D3.js 的可重用 JavaScript 图表库。几乎零学习曲线。star:4.5K(6.28更新)


    • img2css 将图片转为纯css代码。(11.3更新)

    模块管理/加载器

    • ESL 是一个浏览器端、符合AMD的标准加载器,适合用于现代Web浏览器端应用的入口与模块管理。

    ESL vs RequireJS

    体积更小 (Smaller)
    性能更高 (Higher performance)
    更健壮 (More Robustness)
    不支持在非浏览器端使用 (Browser only)
    依赖模块用时定义 (Lazy define)
    
    • seajs 一个遵循CommonJS规范的JavaScript模块加载器。提供简单、极致的模块化开发体验。star:4100


    • Component 一个模块化的JavaScript框架,同时也是面向前端的包管理器。


    • webpack一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。star:9K

    动画

    • animate.css 一个跨浏览器的CSS动画库。简单易用易上手。star:23000


    • move.js 极小的 JavaScript 库,支持 CSS3 的动画效果,非常简单优雅。star:2600


    • TweenJS 是一个简单但强大的 Javascript 动画库。CreateJS 套件的一部分。star:1500


    • bounce.js 一个用于制作漂亮的 CSS3 关键帧动画的 JavaScript
      库,使用其特有的方式生成的动画效果。star:3600


    • Swipe 号称最精确的Slider触摸库,专为移动设备优化。star:4.7K


    • tween.js 一款可生成平滑动画效果的js动画库。tween.js允许你以平滑的方式修改元素的属性值。它可以通过设置生成各种类似CSS3的动画效果。star:2.5K(7.15更新)


    • parallax.js轻量级的的视差引擎,能对智能设备的方向作出反应。。star:9K (10.17更新)
    • Velocity 是一款和jQuery的$.animate()有相同API的动画引擎。很适合移动端的动画开发,还打包了颜色动画,转换,循环,easing效果,类动画、滚动等功能。star:9.5K(12月25更新)

    插件



    • onepage-scroll 可以轻松建立一个动感的响应式的滚动效果页面,比较适用于单页面的专题站。支持现代浏览器和IE8以上版本。View demostar:7700


    • slick 一款完全响应式的 jQuery 图片滚动插件,能够根据容器自动适应宽度。star:10000view demo


    • superslides 致力于解决网站大部分特效展示问题。网站上常用的“焦点图/幻灯片”“Tab标签切换”“图片滚动”“无缝滚动”等只需要一个SuperSlide即可解决! view demostar:1100



    • slider 一个jquery完全开源的JavaScript代码库,用户可以开发,调试和深度定制自己的滑块。star:850view demo


    • github-hovercard github 鼠标悬停显示用户,仓库等摘要信息。(10.15更新)


    • onepage-scroll 一款带有背景视觉差效果的jQuery整页滚动特效插件。star:8K (10.17更新)view demo


    • justlazy.js 轻量级js图片延迟加载插件。(10.22更新)
  • 框架、库和组件

    • polymerweb组件构建框架。一套以“一切皆组件、最少化代码量、最少框架限制”为设计理念的Web UI框架。 star:9900


    • impress.js 创建令人兴奋的演示。使用CSS3的转换和过渡,这个库允许你创建令人印象深刻的演示文稿。view demostar:24300


    • ionic 先进的HTML5 移动端开发框架。帮助开发者使用HTML5, CSS3和js做出不可思议的hybrid appstar:17000


    • reveal.js 基于CSS3的3D幻灯片工具。能够制作绚丽的演示文稿并生成HTML格式,将它发布到web上。star:21500view demo


    • pure.css 一组很小的,响应式的css组件,你可以在网页的项目上到处使用。star:12000


    • three.jsJavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。star:20000



    • jquery-pjaxajaxpushState的封装,让你可以很方便的使用pushState技术,用以实现页面无刷新加载。star:11500


    • highlight.jsjavascript语法高亮。既可以运行在浏览器端也可以运行在服务端。star:5500



    • togetherjsMozilla打造的一款可以给网站添加实时协作功能的JavaScript库。star:5K



    • MEAN.JS 全栈式javascript,使用`MongoDB, Express,
      AngularJSNode.jsstar:2.2K`


    • wechat.js 微信相关的 js 操作:分享、网络、菜单。star:700



    • progress.js 一个 jsCSS3的库,帮助开发人员为网页上的每个对象创建和管理进度条效果。star:1.6Kview demo


    • foundation 号称世界上最先进的响应式前端框架,也是一款Mobile First的框架。star:21K


    • Sugar 一个JavaScript库。它扩展了现有的JS对象的方法,让你可以用更少的代码做更多的事情。star:2.8K


    • todomvc 帮你挑选一款MV*框架,它使用不同的最流行的js MV*框架实现了一个相同的Todo应用。star:13K


    • yepnope.js 这是一个异步的条件加载框架,速度超快,只为用户加载需要的脚本。使用非常简单,非常有用!star:2.5K


    • Material UI 是一个 CSS 框架和一组实现谷歌 Material Design 设计规范的 React
      组件。star:8.8K(6.28更新)


    • Pikaday 是一个 JavaScript 日期选择器,特点是轻量级、无依赖和模块化的
      CSSstar:2.8K [view demo]104

    图片描述

    • vuejs 用于构建交互式的 Web 界面的库。它提供了 MVVM 数据绑定和一个可组合的组件系统,具有简单、灵活的 API。star:6K(7.12更新)


    • meteor 超简单的,数据库无处不在的,用于自动化和简化实时运行的 Web 应用程序的开发。纯JavaScript的Web框架。star:27K(7.12更新)


    • webuploader
      一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。支持大文件分片并发上传,极大的提高了文件上传效率。star:1.8K


    • fastclick触摸UI上的消除点击延迟js库。star:1W(10.15更新)


    • wangEditor 轻量级web富文本框。 (10.15更新)


    • benchmark.js是强大的基准测试库,几乎适用于所有JavaScript平台。支持high-resolution定时器,并返回重要的统计结果。star:1.7k(10.15更新)


    • headroom.js是一个轻量级、纯 JS 组件,用来隐藏或展现页面上的元素,为你的页面留下更多展示内容的空间。star:7.5K (10.15更新)


    移动端

    • Swipe 加速移动触摸滑块与硬件之间的转换。star:5000


    • hammer.js 一个支持多点触摸的手势库。star:11000


    • amDoc 无线Web解决方案 - 文档规范指南


    • amazeui 移动优先的跨屏前端框架。面向HTML5开发,使用css3做动画和交互。star:4.1K


    • Zepto 一款面向移动端设备、APIjQuery兼容的基础库。


    • mui 最接近原生APP体验的高性能框架 。star:1.1K(6.28更新)


    • Swiperjavascript打造的滑动特效插件,面向手机、平板电脑等移动终端。能实现触屏焦点图、触屏Tab切换、触屏多图切换等常用效果。star:6K(10月18更新)


    • SUI-Mobile由阿里巴巴国际UED前端出品的移动端UI库,轻量精美 star:2K(2016.1.14更新)


    • lib-flexible 淘宝出品的移动端可伸缩布局方案。star:1.3K+(2016.1.14更新)

    Node.js相关

    http://nodeframework.com/ 专门收集node.jsweb框架的网站。其项目同样均托管在github上。
    图片描述

    • nodeclub 使用 Node.jsMongoDB 开发的社区系统。star:3000


    • N-chat 使用 Express + Socket.IO 搭建的多人聊天室 。star:300


    • N-blog 使用 Express + MongoDB 搭建多人博客。star:1800


    • node-inspector 基于Blink开发者工具的Node.js调试器。star:7000



    • nodePPT 使用nodejs写的网络幻灯片。可能是迄今为止最好的网页版PPTstar:1.6Kview demo


    • hexo 一款快捷,简单,强大的博客框架,基于Nodejsstar:5.2K


    • koa 下一代Node.js Web 框架。由 Express 团队设计。star:6.3K



    • connectNode平台的中间件框架。Express就是基于Connect开发的。star:5.6K(6.29更新)


    • n node版本管理,tj大神所写。star:2.7K


    • nvm node版本管理,通过bash脚本来管理。star:7.5K
    • nodemon这个库是专门调试时候使用的,它会自动检测 node.js 代码的改动,然后帮你自动重启应用。star:7K+。(2.24.16)
    • supertestAPI使用流利的API测试node.js http 服务器。3.1K+

    React相关



    • react-native 一个用React构建native apps的框架。star:15000



    • awesome-react 关于react的工具,资源,视频的集合。star:700


    • Flux 是一个Facebook开发的、利用单向数据流实现的应用架构,用于
      ReactFlux应用有三个主要的部分组成:调度程序、存储和视图(React 组件)。star:6.8K(6.28更新)
    • iscroll 高性能,体积小,无外部依赖,跨平台的滚动组件 star:6K(7.19更新)
    • react-tappable Tappable component for React.(2016,1.4更新)
    • react-native-lessonreact-native入门指南.star:1.3K(2016,1.4更新)

    HTML5

    • html5-boilerplate 一套专业的前端模版,主要用于开发快速、健壮、适应性强的app或网站。star:27K (12.25更新)
    • BrowserquestMozilla开发的HTML5多人在线游戏。star:5200
    • video.js 开源的HTML5和Flash视频播放器。支持自定义进度条、按钮以及工具栏的底色。star:9.1K


    • html5shiv 主要解决HTML5提出的新的元素不被IE6-9识别。star:6K(7.15更新)


    • brunch 快速的前端 HTML5 构建工具。star:4.5K(7.19更新)


    • ulkit 一个轻量级的、模块化前端框架,它被用于快速开发强大的web界面。也是一款优秀的响应式HTML5 框架。star:5.3K (8.3更新)

    模板引擎

    • Handlebars.js 一个js语义模板库,能让你轻松高效的编写语义化模板。star:8.6K(6.29 update)


    • artTemplate 性能卓越的 js 模板引擎。star:1.7K


    • jade 一款高性能简洁易懂的模板引擎,JadeHamlJavascript实现。star:8.7K
    • ejs tj大神写的嵌入javascript的模板引擎,主要用于Node

    浏览器兼容方案

    • es6-shim 提供兼容性垫片,使ES6能兼容于传统的JavaScript引擎。star:1.5K(7.15更新)


    • Modernizr 用来检测浏览器功能支持情况的JavaScript库,可以检测18项CSS3功能以及40多项关于HTML5的功能。star:16000


    • normalize.css 一个可定制的 CSS 文件,使浏览器呈现的所有元素,更一致和符合现代标准。支持IE8+。star:17000


    • html5shiv 主要解决HTML5提出的新的元素不被IE6-9识别。star:6K(7.15更新)



    • Babel 是一款为了写下一代js的编译器,无需等待浏览器支持就可以使用新的语法。star:8.3K (7.29更新)

    高产大牛

    • Evan You 前端轻量级框架MVVM框架vue.js作者,前Google工程师。followers:1.6K


    • TJ Holowaychuk Luna 编程语言, Koa, Express, Stylus, Cluster, Mocha, Jade, node-canvas, component 等知名开源项目的创建和贡献者。 followers:14.1K


    • PaulIrish 著名的前端开发工程师,同时他也是Chrome开发者关系团队成员,jQuery团队成员,Modernizr、Yeoman、CSS3 PleaseHTML5 Boilerplatelead developerfollowers:15.7K


    • Mike Bostock 知名可视化库 D3.js的主要作者。followers:8.3K



    • 司徒正美 前端迷你MVVM框架Avalon作者。followers:2.1K

    其他


    • Mars 腾讯移动Web前端知识库。star:1600


    • brackets 一款使用 HTML,CSS,JavaScript 创建的开源的针对 Web 开发的编辑器。star:23000


    • GhostNode.js开发最新博客系统, 简单简洁, 响应式设计, 支持完全自定义, 免费, 专注博客。star:16000


    • io.jsNodeJS里分离出来的一条分支。star:13000



    • ueditor 百度前端团队出品的富本文编辑器。star:1.1K

    优秀开源组织

    PS:文章在github更新,本篇文章停止更新

    查看原文

    赞 378 收藏 1821 评论 26

    不懂的小兵 赞了文章 · 2015-05-21

    JavaScript 调试常见报错以及修复方法

    (看到一篇调试JS很有用的文章,收藏一下)

    JavaScript 调试是一场噩梦:首先给出的错误非常难以理解,其次给出的行号不总有帮助。有个查找错误含义,及修复措施的列表,是不是很有用?

    以下是奇怪的 JavaScript 错误列表。同样的错误,不同的浏览器会给出不同的消息,因此有一些不同的例子。

    如何读懂错误?

    首先,让我们快速看下错误信息的结构。理解结构有助于理解错误,如果遇到列表之外的错误会减少麻烦。

    Chrome 中典型的错误像这样:

    Uncaught TypeError: undefined is not a function
    

    错误的结构如下:

    1. Uncaught TypeError: 这部分信息通常不是很有用。Uncaught 表示错误没有被 catch 语句捕获,TypeError 是错误的名字。
    2. undefined is not a function: 这部分信息,你必须逐字阅读。比如这里表示代码尝试使用 undefined ,把它当做一个函数。

    其它基于 webkit 的浏览器,比如 Safari ,给出的错误格式跟 Chrome 很类似。Firefox 也类似,但是不总包含第一部分,最新版本的 IE 也给出比 Chrome 简单的错误 - 但是在这里,简单并不总代表好。

    以下是真正的错误。

    Uncaught TypeError: undefined is not a function

    相关错误:

    number is not a function, object is not a function, string is not a function, Unhandled Error: ‘foo’ is not a function, Function Expected
    

    当尝试调用一个像方法的值时,这个值并不是一个方法。比如:

    var foo = undefined;
    foo();
    

    如果你尝试调用一个对象的方法时,你输错了名字,这个典型的错误很容易发生。

    var x = document.getElementByID('foo');
    

    由于对象的属性不存在,默认是 undefined ,以上代码将导致这个错误。尝试调用一个像方法的数字,“number is not a function” 错误出现。

    如何修复错误:确保方法名正确。这个错误的行号将指出正确的位置。

    Uncaught ReferenceError: Invalid left-hand side in assignment

    相关错误:

    Uncaught exception: ReferenceError: Cannot assign to ‘functionCall()’, Uncaught exception: ReferenceError: Cannot assign to ‘this’
    

    尝试给不能赋值的东西赋值,引起这个错误。

    这个错误最常见的例子出现在 if 语句使用:

    if(doSomething() = 'somevalue')
    

    此例中,程序员意外地使用了单个等号,而不是双等号。“left-hand side in assignment” 提及了等号左手边的部分,因此你可以看到以上例子,左手边包含不能赋值的东西,导致这个错误。

    如何修复错误:确保没有给函数结果赋值,或者给 this 关键字赋值。

    Uncaught TypeError: Converting circular structure to JSON

    相关错误:

    Uncaught exception: TypeError: JSON.stringify: Not an acyclic Object, TypeError: cyclic object value, Circular reference in value argument not supported
    

    把循环引用的对象,传给 JSON.stringify 总会引起错误。

    var a = { };
    var b = { a: a };
    a.b = b;
    JSON.stringify(a);
    

    由于以上的 ab 循环引用彼此,结果对象无法转换成 JSON。

    如何修复错误: 移除任何想转换成 JSON 的对象中的循环引用。

    Unexpected token ;

    相关错误:

    Expected ), missing ) after argument list
    

    JavaScript 解释器预期的东西没有被包含。不匹配的圆括号或方括号通常引起这个错误,错误信息可能有所不同 - “Unexpected token ]” 或者 “Expected {” 等。

    如何修复错误: 有时错误出现的行号并不准确,因此很难修复。

    • [ ]{ }( ) 这几个符号不配对常常导致出错。检查所有的圆括号和方括号是否配对。行号指出的不仅是问题字符。
    • Unexpected / 跟正则表达式有关。此时行号通常是正确的。
    • Unexpected ; 对象或者数组字面量里面有个;通常引起这个错误,或者函数调用的参数列表里有个分号。此时的行号通常也是正确的。

    Uncaught SyntaxError: Unexpected token ILLEGAL

    相关错误:

    Unterminated String Literal, Invalid Line Terminator
    

    一个字符串字面量少了结尾的引号。

    如何修复错误: 确保所有的字符串都有结束的引号。

    Uncaught TypeError: Cannot read property ‘foo’ of null, Uncaught TypeError: Cannot read property ‘foo’ of undefined

    相关错误:

    TypeError: someVal is null, Unable to get property ‘foo’ of undefined or null reference
    

    尝试读取 null 或者 undefined ,把它当成了对象。例如:

    var someVal = null;
    console.log(someVal.foo);
    

    如何修复错误: 通常由于拼写错误导致。检查错误指出的行号附近使用的变量名是否正确。

    Uncaught TypeError: Cannot set property ‘foo’ of null, Uncaught TypeError: Cannot set property ‘foo’ of undefined

    相关错误:

    TypeError: someVal is undefined, Unable to set property ‘foo’ of undefined or null reference
    

    尝试写入 null 或者 undefined ,把它当成了一个对象。例如:

    var someVal = null;
    someVal.foo = 1;
    

    如何修复错误: 也是由于拼写错误所致。检查错误指出的行号附近的变量名。

    Uncaught RangeError: Maximum call stack size exceeded

    相关错误:

    Related errors: Uncaught exception: RangeError: Maximum recursion depth exceeded, too much recursion, Stack overflow
    

    通常由程序逻辑 bug 引起,导致函数的无限递归调用。

    如何修复错误: 检查递归函数中可能导致无限循环 的 bug 。

    Uncaught URIError: URI malformed

    相关错误:

    URIError: malformed URI sequence
    

    无效的 decodeURIComponent 调用所致。

    如何修复错误: 按照错误指出的行号,检查 decodeURIComponent 调用,它是正确的。

    XMLHttpRequest cannot load [http://some/url/](http://some/url/). No ‘Access-Control-Allow-Origin’ header is present on the requested resource

    相关错误:

    Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at
    [http://some/url/](http://some/url/)
    

    错误肯定是使用 XMLHttpRequest 引起的。

    如何修复: 确保请求的 URL 是正确的,它遵循同源策略 。最好的方法是从代码中找到错误信息指出的 URL 。

    InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

    相关错误:

    InvalidStateError, DOMException code 11
    

    代码调用的方法在当前状态无法调用。通常由 XMLHttpRequest 引起,在方法准备完毕之前调用它会引起错误。

    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader('Some-Header', 'val');
    

    这时就会出错,因为 setRequestHeader 方法只能在 xhr.open 方法之后调用。

    如何修复: 查看错误指出的行号,确保代码运行的时机正确,或者在它(例如 xhr.open)之前添加了不必要的调用

    结论

    我看过不少无用的 JavaScript 错误,比如 PHP 中声名狼藉的异常 Expected T_PAAMAYIM_NEKUDOTAYIM 。抛出更熟悉的错误才更有意义。现代浏览器不再抛出完全无用的错误,才会更有帮助。



    原文Jani Hartikainen - 《JavaScript Errors and How to Fix Them
    翻译出处:涂鸦码农 - JavaScript 错误以及如何修复

    查看原文

    赞 25 收藏 129 评论 0

    不懂的小兵 回答了问题 · 2014-09-11

    解决关于图片列表中图片规格不一,怎么用css来解决。

    这种排列方式,是在做瀑布流么,那么建议给img加上width:100% 就好了,高度会自动按比例适应

    关注 2 回答 5

    认证与成就

    • 获得 0 次点赞
    • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

    擅长技能
    编辑

    (゚∀゚ )
    暂时没有

    开源项目 & 著作
    编辑

    (゚∀゚ )
    暂时没有

    注册于 2014-08-20
    个人主页被 319 人浏览