详解vue-cli 2.0配置文件(小结)


Posted in Javascript onJanuary 14, 2019

上次给大家分享的是用vue-cli快速搭建vue项目,虽然很省时间和精力,但想要真正搞明白,我们还需要对其原理一探究竟。

大家拿到一个项目,要快速上手,正确的思路是这样的:

首先,如果在项目有readme.md的情况下,大家要先读readme,项目的一些基本介绍,包括项目信息、运行的脚本、采用何种框架,以及项目维护者等信息通常都会有。一般在git上维护的项目都会有readme.md,不熟悉markdown语法的同学可以先了解下markdown入门。

第二步,要看package.json。现代的前端项目中通常都会有package.json文件。在package.json里,会介绍项目名称、版本、描述、作者、脚本、依赖包,对环境的要求,以及对浏览器要求。

{
 "name": "uccn",
 "version": "1.0.0",
 "description": "uccn3.0",
 "author": "v_yangtianjiao <v_yangtianjiao@baidu.com>",
 "private": true,
 // 这里的脚本是分析项目的主要入口
 "scripts": {
 "dev": "node build/dev-server.js",
 "start": "node build/dev-server.js",
 "build": "node build/build.js",
 "jsonp": "node build/jsonp-server.js"
 },

 // 项目依赖
 "dependencies": {
 "fetch-jsonp": "^1.1.3",
 "less": "^2.7.2",
 "less-loader": "^4.0.4",
 "stylus": "^0.54.5",
 "stylus-loader": "^3.0.1",
 "vue": "^2.4.2"
 },
 "devDependencies": {
 "autoprefixer": "^7.1.2",
 "babel-core": "^6.22.1",
 "babel-loader": "^7.1.1",
 "babel-plugin-component": "^0.10.1",
 "babel-plugin-transform-runtime": "^6.22.0",
 "babel-preset-env": "^1.3.2",
 "babel-preset-es2015": "^6.24.1",
 "babel-preset-stage-2": "^6.22.0",
 "babel-register": "^6.22.0",
 "chalk": "^2.0.1",
 "connect-history-api-fallback": "^1.3.0",
 "copy-webpack-plugin": "^4.0.1",
 "css-loader": "^0.28.0",
 "cssnano": "^3.10.0",
 "eventsource-polyfill": "^0.9.6",
 "express": "^4.14.1",
 "extract-text-webpack-plugin": "^2.0.0",
 "file-loader": "^0.11.1",
 "friendly-errors-webpack-plugin": "^1.1.3",
 "html-webpack-plugin": "^2.28.0",
 "http-proxy-middleware": "^0.17.3",
 "opn": "^5.1.0",
 "optimize-css-assets-webpack-plugin": "^2.0.0",
 "ora": "^1.2.0",
 "rimraf": "^2.6.0",
 "semver": "^5.3.0",
 "shelljs": "^0.7.6",
 "url-loader": "^0.5.8",
 "vue-loader": "^13.0.4",
 "vue-style-loader": "^3.0.1",
 "vue-template-compiler": "^2.4.2",
 "webpack": "^2.6.1",
 "webpack-bundle-analyzer": "^2.2.1",
 "webpack-dev-middleware": "^1.10.0",
 "webpack-hot-middleware": "^2.18.0",
 "webpack-merge": "^4.1.0"
 },
  // 对node版本的以及npm版本的要求
 "engines": {
 "node": ">= 4.0.0",
 "npm": ">= 3.0.0"
 },

 // 浏览器要求,vue项目不支持ie8,因为ie8是es3,尚没有Object.defineProperty属性
 "browserslist": [
 "> 1%",
 "last 2 versions",
 "not ie <= 8"
 ]
}

上面的package.json是从实际vue项目中摘出来的,大家从package.json中就会对项目有一个大概的了解,最主要的是脚本部分。通过npm的自动化任务,可以很方便的执行配置文件中的脚本。通过配置 "jsonp": "node build/jsonp-server.js",可以方便的使用npm run jsonp命令,代替node build/jsonp-server.js或者更复杂的一系列命令。详细的npm自动化命令可以移步npm 自动化。

详解vue-cli 2.0配置文件(小结)

现在的项目目录结构如上,我们从刚才的脚本入手。首先是启服务的脚本npm run dev,实际上是执行node build/dev-server.js,我们在build文件夹中找到dev-server.js,一步步分析。

/* eslint-disable */

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

// 获取配置文件中默认的配置
var config = require('../config')
// 如果node无法判断当前是开发环境还是生产环境,则使用config.dev.env.NODE_ENV作为当前的环境
if (!process.env.NODE_ENV) {
 process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

var opn = require('opn')// 用来在起来服务之后,打开浏览器并跳转指定URL
var path = require('path')// node自带文件路径工具
var express = require('express')// node框架express(本地开发的核心,起服务)
var webpack = require('webpack')// webpack,压缩打包
var proxyMiddleware = require('http-proxy-middleware')// 中间件
var webpackConfig = require('./webpack.dev.conf')// 开发环境的webpack配置
var mockMiddleware = require('../config/dev.mock')// 开发环境本地mock数据中间件

var port = process.env.PORT || config.dev.port
var autoOpenBrowser = !!config.dev.autoOpenBrowser
var proxyTable = config.dev.proxyTable

var app = express()// 起服务
var compiler = webpack(webpackConfig)// webpack进行编译

// webpack-dev-middleware将编译的文件放在内存中,后续注入
var devMiddleware = require('webpack-dev-middleware')(compiler, {
 publicPath: webpackConfig.output.publicPath,
 quiet: true
})
// 热加载
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
 log: false,
 heartbeat: 2000
})
compiler.plugin('compilation', function (compilation) {
 compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
 hotMiddleware.publish({ action: 'reload' })
 cb()
 })
})

// proxy api requests
// proxyTable中的配置挂载到express中
Object.keys(proxyTable).forEach(function (context) {
 var options = proxyTable[context]
 if (typeof options === 'string') {
 options = { target: options }
 }
 app.use(proxyMiddleware(options.filter || context, options))
})

// 处理后退的时候匹配资源
app.use(require('connect-history-api-fallback')())

// 暂存在内存的webpack编译后的文件挂载到express上
app.use(devMiddleware)

// 将本地mock中间件挂载到express上
app.use(mockMiddleware);

// 热加载挂载到express上
app.use(hotMiddleware)

// 拼static静态资源文件路径
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
// express为静态资源提供服务
app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port

var _resolve
var readyPromise = new Promise(resolve => {
 _resolve = resolve
})

console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
 console.log('> Listening at ' + uri + '\n')
 if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
 opn(uri)
 }
 _resolve()
})
// 通过配置的端口,自动打开浏览器,并跳转拼好的URL,至此,发开环境已经跑起来了
var server = app.listen(port)

module.exports = {
 ready: readyPromise,
 close: () => {
 server.close()
 }
}

在上面的dev-server中,有很多变量来自于./config/index.js和webpack.dev.conf.js,我们一个个看上述配置文件。

首先看./config/index.js,这里是整个项目主要的配置入口,我们在代码中一步步分析:

// node自带路径工具.
var path = require('path')
// 分为两种环境,dev和production
module.exports = {
 build: {
 env: require('./prod.env'),// 使用config/prod.env.js中定义的编译环境
 index: path.resolve(__dirname, '../dist/index.html'),// 编译输入的index.html文件。node.js中,在任何模块文件内部,可以使用__filename变量获取当前模块文件的带有完整绝对路径的文件名,
 assetsRoot: path.resolve(__dirname, '../dist'),// 编译输出的静态资源路径
 assetsSubDirectory: 'static',// 编译输出的二级目录
 assetsPublicPath: './', // 编译发布的根目录,可配置为资源服务器或者cdn域名
 productionSourceMap: false,//是否开启cssSourceMap
 productionGzip: false,// 是否开启gzip
 productionGzipExtensions: ['js', 'css'],// 需要用gzip压缩的文件扩展名
 bundleAnalyzerReport: process.env.npm_config_report
 },
 dev: {
 env: require('./dev.env'),
 port: 8989,// 起服务的端口
 autoOpenBrowser: true,
 assetsSubDirectory: 'static',
 assetsPublicPath: '/',
 proxyTable: {},// 需要代理的接口,可以跨域
 cssSourceMap: false
 }
}

接着我们分析webpack.dev.conf.js:

var utils = require('./utils')// 工具类
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')// 使用webpack配置合并插件
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')// 这个插件自动生成HTML,并注入到.html文件中
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

// 将hot-reload相对路径添加到webpack.base.conf的对应的entry前面
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
 baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})

// webpack.dev.conf.js与webpack.base.conf.js中的配置合并
module.exports = merge(baseWebpackConfig, {
 module: {
 rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
 },
 // webpack-devtool有7种模式,cheap-module-eval-source-map模式是比较快的开发模式
 
 devtool: '#cheap-module-eval-source-map',
 plugins: [
// 你可以理解为,通过配置了DefinePlugin,那么这里面的标识就相当于全局变量,你的业务代码可以直接使用配置的标识。
 new webpack.DefinePlugin({
  'process.env': config.dev.env
 }),
 // hotModule插件让页面变动时,只重绘对应的模块,不会重绘整个HTML文件
 new webpack.HotModuleReplacementPlugin(),

// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误
 new webpack.NoEmitOnErrorsPlugin(),
 // 将生成的HTML代码注入index.html文件
 new HtmlWebpackPlugin({
  filename: 'index.html',
  template: 'index.html',
  inject: true
 }),

// friendly-errors-webpack-plugin用于更友好地输出webpack的警告、错误等信息
 new FriendlyErrorsPlugin()
 ]
})

刚才的webpack.dev.conf.js中有引到webpack.base.conf.js,我们就把他们一网打尽,继续看webpack.base.conf.js!

/* eslint-disable */
var path = require('path')// node自带的文件路径插件
var utils = require('./utils')// 工具类
var config = require('../config')// 上面说过的config/index
var vueLoaderConfig = require('./vue-loader.conf')// vue-loader.conf配置文件是用来解决各种css文件的,定义了诸如css,less,sass之类的和样式有关的loader
// 此函数是用来返回当前目录的平行目录的路径,
function resolve (dir) {
 return path.join(__dirname, '..', dir)
}

module.exports = {
 entry: {
 uccn: './src/main.js'// 入口
 },
 output: {
// 路径是config目录下的index.js中的build配置中的assetsRoot,也就是dist目录
 path: config.build.assetsRoot,
 filename: '[name].js',

// 上线地址,也就是真正的文件引用路径,如果是production生产环境,其实这里都是 '/'
 publicPath: process.env.NODE_ENV === 'production'
  ? config.build.assetsPublicPath
  : config.dev.assetsPublicPath
 },
 // resolve是webpack的内置选项,顾名思义,决定要做的事情,也就是说当使用 import "jquery",该如何去执行这件事情,就是resolve配置项要做的,import jQuery from "./additional/dist/js/jquery" 这样会很麻烦,可以起个别名简化操作
 resolve: {

// 省略扩展名,比方说import index form '../js/index', 会默认去找index文件,然后找index.js,.vue,.josn.
 extensions: ['.js', '.vue', '.json'],
 alias: {
  'vue$': 'vue/dist/vue.esm.js',


// 使用上面的resolve函数,意思是用@代替src的绝对路径
  '@': resolve('src'),
 }
 },
 // 不同的模块使用不同的loader
 module: {
 rules: [
  {


 // 对vue文件,使用vue-loader解析
  test: /\.vue$/,
  loader: 'vue-loader',
  options: vueLoaderConfig
  },
  {


 // babel-loader把es6解析成es5
  test: /\.js$/,
  loader: 'babel-loader',
  include: [resolve('src'), resolve('test')]
  },
  {


 // url-loader将文件大小低于下面option中limit的图片,转化为一个64位的DataURL,这样会省去很多请求,大于limit的,按[name].[hash:7].[ext]的命名方式放到了static/img下面,方便做cache
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  loader: 'url-loader',
  options: {
   limit: 20000,
   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]')
  }
  }
 ]
 }
}

至此,npm run dev起本地开发环境相关的配置文件基本说完了,接着说一下上面都用到的util工具类:

var path = require('path')
var config = require('../config')
// extract-text-webpack-plugin该插件的主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象
var ExtractTextPlugin = require('extract-text-webpack-plugin')

// 返回资源文件路径,path.posix以posix兼容的方式交互,是跨平台的,如果是path.win32的话,只能在win上
exports.assetsPath = function (_path) {
 var assetsSubDirectory = process.env.NODE_ENV === 'production'
 ? config.build.assetsSubDirectory
 : config.dev.assetsSubDirectory
 return path.posix.join(assetsSubDirectory, _path)
}

// 通过判断是否是生产环境,配置不同的样式语言的loader配置
exports.cssLoaders = function (options) {
 options = options || {}

 var cssLoader = {
 loader: 'css-loader',
 options: {
  minimize: process.env.NODE_ENV === 'production',
  sourceMap: options.sourceMap
 }
 }

 // 生成各种loader配置,通过传入不同的loader和option,将不同样式文件语言的loader拼好,push到loader配置中。
 function generateLoaders (loader, loaderOptions) {
 var loaders = [cssLoader]
 if (loader) {
  loaders.push({
  loader: loader + '-loader',
  options: Object.assign({}, loaderOptions, {
   sourceMap: options.sourceMap
  })
  })
 }

 // extract-text-webpack-plugin有三个参数,use指需要用什么loader去编译文件;fallback指编译后用什么loader去提取文件;还有一个publicfile用来覆盖项目路径
 if (options.extract) {
  return ExtractTextPlugin.extract({
  use: loaders,
  fallback: 'vue-style-loader'
  })
 } else {
  return ['vue-style-loader'].concat(loaders)
 }
 }

 // 对不同的样式语言,返回相应的loader
 return {
 css: generateLoaders(),
 postcss: generateLoaders(),
 less: generateLoaders('less'),
 sass: generateLoaders('sass', { indentedSyntax: true }),
 scss: generateLoaders('sass'),
 stylus: generateLoaders('stylus'),
 styl: generateLoaders('stylus')
 }
}

// 生成处理不同的样式文件处理规则
exports.styleLoaders = function (options) {
 var output = []
 var loaders = exports.cssLoaders(options)
 for (var extension in loaders) {
 var loader = loaders[extension]
 output.push({
  test: new RegExp('\\.' + extension + '$'),
  use: loader
 })
 }
 return output
}

———————————————— 华丽的分隔符 —————————————————

下面我们继续说npm run build,打包编译的一系列操作~

从package.json 中可以看出,npm run build,其实是执行了 node build/build.js,我们在build文件夹中找到build.js,build主要的工作是:检测node和npm版本,删除dist包,webpack构建打包,在终端输出构建信息并结束,如果报错,则输出报错信息

require('./check-versions')()

process.env.NODE_ENV = 'production'

// 在终端显示的旋转器插件
var ora = require('ora')
// 用于删除文件夹
var rm = require('rimraf')
var path = require('path')
// 终端文字颜色插件
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')

var spinner = ora('building for production...')
spinner.start()

// 删除dist文件夹,之后webpack打包
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
 if (err) throw err
 webpack(webpackConfig, function (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'
 ))
 })
})

build.js用到了webpack.prod.conf.js,他与webpack.base.conf.js merge之后,作为webpack配置文件,我们再看看webpack.prod.conf.js,主要做的工作是:

1.提取webpack生成的bundle中的文本,到特定的文件,使得css,js文件与webpack输出的bundle分离。

2.合并基本的webpack配置

3.配置webpack的输出,包括输出路径,文件名格式。

4.配置webpack插件,包括丑化代码。

5.gzip下引入compression插件进行压缩。

/* eslint-disable */
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
// 用于从webpack生成的bundle中提取文本到特定文件中的插件
// 可以抽取出css,js文件将其与webpack输出的bundle分离
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

var env = config.build.env
// 合并基础的webpack配置
var webpackConfig = merge(baseWebpackConfig, {
 module: {
 rules: utils.styleLoaders({
  sourceMap: config.build.productionSourceMap,
  extract: true
 })
 },
 // 7中sourceMap上面有讲过
 devtool: config.build.productionSourceMap ? '#source-map' : false,
 // 配置webpack输出的目录,及文件命名规则
 output: {
 path: config.build.assetsRoot,
 filename: utils.assetsPath('js/[name].min.js'),
 chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
 },
 // webpack插件配置
 plugins: [
 // 同webpack.dev.conf.js
 new webpack.DefinePlugin({
  'process.env': env
 }),
// 丑化代码
 new webpack.optimize.UglifyJsPlugin({
  compress: {
  warnings: false
  },
  sourceMap: true
 }),
 // 抽离css文件到单独的文件
 new ExtractTextPlugin({
  filename: utils.assetsPath('css/[name].min.css')
 }),
 new OptimizeCSSPlugin({
  cssProcessorOptions: {
  safe: true
  }
 }),
 // 生成并注入index.html
 new HtmlWebpackPlugin({
  filename: config.build.index,
  template: 'index.html',
  inject: true,
  minify: {
  removeComments: true,
  collapseWhitespace: false,
  removeAttributeQuotes: true
  },
  chunksSortMode: 'dependency'
 }),
 // keep module.id stable when vender modules does not change
 new webpack.HashedModuleIdsPlugin(),
 split vendor js into its own file
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
  // any required modules inside node_modules are extracted to vendor
  return (
   module.resource &&
   /\.js$/.test(module.resource) &&
   module.resource.indexOf(
   path.join(__dirname, '../node_modules')
   ) === 0
  )
  }
 }),
 extract webpack runtime and module manifest to its own file in order to
 prevent vendor hash from being updated whenever app bundle is updated
 new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest',
  chunks: ['vendor']
 }),
 copy custom static assets
 new CopyWebpackPlugin([
  {
  from: path.resolve(__dirname, '../static'),
  to: config.build.assetsSubDirectory,
  ignore: ['.*']
  }
 ])
 ]
})
// gzip模式下需要引入compression插件进行压缩
if (config.build.productionGzip) {
 var CompressionWebpackPlugin = require('compression-webpack-plugin')

 webpackConfig.plugins.push(
 new CompressionWebpackPlugin({
  asset: '[path].gz[query]',
  algorithm: 'gzip',
  test: new RegExp(
  '\\.(' +
  config.build.productionGzipExtensions.join('|') +
  ')$'
  ),
  threshold: 10240,
  minRatio: 0.8
 })
 )
}

if (config.build.bundleAnalyzerReport) {
 var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
 webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

到此为止,vue官方脚手架工具vue-cli 2.0的所有配置文件都已介绍完毕,从头到尾再梳理一遍:

执行npm run dev或者npm run start,实际是在node环境执行build/dev-server.js, dev-server.js会去拿到config中的端口等配置,通过express起一个服务,通过插件自动打开浏览器,加载webpack编译后放在内存的bundle。

执行npm run build,实际上执行了build/build.js,通过webpack的一系列配置及插件,将文件打包合并丑化,并创建dist目录,放置编译打包后的文件,这将是未来用在生产环境的包。

写这篇文章我自身的收获也挺多,第一是对vue-cli整体的认知更加清晰条理,第二是对webpack的一些插件有了新的认识。以前对一些插件模棱两可,直接越过,这是不对的,要一步一个脚印儿,遇坑填坑,这样才会有收获。虽然过程可能是艰辛的,但收获将会是巨大的~希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript获取窗口(容器)的大小及位置参数列举及简要说明
Dec 09 Javascript
jquery子元素过滤选择器使用示例
Jun 24 Javascript
输入自动提示搜索提示功能的使用说明:sugggestion.txt
Sep 02 Javascript
jquery隔行换色效果实现方法
Jan 15 Javascript
javascript中JSON.parse()与eval()解析json的区别
May 19 Javascript
使用jQuery.Qrcode插件在客户端动态生成二维码并添加自定义Logo
Sep 01 Javascript
完美实现js焦点轮播效果(二)(图片可滚动)
Mar 07 Javascript
详解关闭令人抓狂的ESlint 语法检测配置方法
Oct 28 Javascript
微信小程序 this.triggerEvent()的具体使用
Dec 10 Javascript
ES2020 新特性(种草)
Jan 12 Javascript
js实现石头剪刀布游戏
Oct 11 Javascript
vue中axios封装使用的完整教程
Mar 03 Vue.js
使用vue-cli脚手架工具搭建vue-webpack项目
Jan 14 #Javascript
vue-cli系列之vue-cli-service整体架构浅析
Jan 14 #Javascript
JS数组求和的常用方法总结【5种方法】
Jan 14 #Javascript
JS实现根据数组对象的某一属性排序操作示例
Jan 14 #Javascript
vue项目中使用vue-i18n报错的解决方法
Jan 13 #Javascript
vscode下vue项目中eslint的使用方法
Jan 13 #Javascript
jQuery实现的中英文切换功能示例
Jan 11 #jQuery
You might like
PHP FOR MYSQL 代码生成助手(根据Mysql里的字段自动生成类文件的)
2011/07/23 PHP
PHP的命令行命令使用指南
2015/08/18 PHP
php微信开发之关注事件
2018/06/14 PHP
php创建类并调用的实例方法
2019/09/25 PHP
PHP isset()及empty()用法区别详解
2020/08/29 PHP
简单实现js页面切换功能
2021/01/10 Javascript
easyui combogrid实现本地模糊搜索过滤多列
2017/05/13 Javascript
layer插件select选中默认值的方法
2018/08/14 Javascript
bootstrap实现点击删除按钮弹出确认框的实例代码
2018/08/16 Javascript
Vue + Element UI图片上传控件使用详解
2019/08/20 Javascript
layui动态绑定事件的方法
2019/09/20 Javascript
原生JS实现天气预报
2020/06/16 Javascript
解决Vue-Router升级导致的Uncaught (in promise)问题
2020/08/07 Javascript
Python中扩展包的安装方法详解
2017/06/14 Python
浅谈使用Python变量时要避免的3个错误
2017/10/30 Python
Python实现PS滤镜Fish lens图像扭曲效果示例
2018/01/29 Python
python解决字符串倒序输出的问题
2018/06/25 Python
Python 使用类写装饰器的小技巧
2018/09/30 Python
Python中的异常处理try/except/finally/raise用法分析
2019/02/28 Python
django-allauth入门学习和使用详解
2019/07/03 Python
Python2.7:使用Pyhook模块监听鼠标键盘事件-获取坐标实例
2020/03/14 Python
python不同系统中打开方法
2020/06/23 Python
Django如何继承AbstractUser扩展字段
2020/11/27 Python
HTML5的结构和语义(5):交互
2008/10/17 HTML / CSS
HTML5的表单(绝对特别强大的功能)使用示例
2013/06/20 HTML / CSS
Perfume’s Club法国站:购买香水和化妆品
2019/05/02 全球购物
党员自我评价范文2015
2015/03/03 职场文书
2015年万圣节活动总结
2015/03/24 职场文书
中学生勤俭节约倡议书
2015/04/29 职场文书
中学教师读书笔记
2015/07/01 职场文书
战友聚会致辞
2015/07/28 职场文书
学习经验交流会总结
2015/11/02 职场文书
干货!开幕词的写作方法
2019/04/02 职场文书
七年级写作指导之游记作文
2019/10/07 职场文书
Python&Matlab实现樱花的绘制
2022/04/07 Python
MySQL的表级锁,行级锁,排它锁和共享锁
2022/07/15 MySQL