├── .babelrc ├── .gitignore ├── READEME.md ├── README.md ├── build ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js ├── favicon.ico ├── header.html ├── index.html ├── package.json ├── postcss.config.js ├── proce.js ├── src ├── App.vue ├── api │ ├── api.js │ ├── http.js │ └── interface.js ├── assets │ ├── css │ │ ├── index.scss │ │ └── modules │ │ │ ├── common.scss │ │ │ ├── config.scss │ │ │ ├── normalize.scss │ │ │ └── reset.scss │ ├── iconfont │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ └── images │ │ ├── aside │ │ ├── 1.jpg │ │ └── 2.jpg │ │ └── login │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ └── 4.jpg ├── components │ ├── Hello.vue │ ├── ImgVerify │ │ └── index.vue │ └── index.js ├── index.js ├── mock │ └── index.js ├── router │ └── index.js ├── store │ └── index.js └── views │ ├── layout │ ├── aside.vue │ ├── header.vue │ └── index.vue │ └── pages │ ├── login │ └── index.vue │ ├── menu │ ├── five.vue │ ├── four.vue │ ├── one.vue │ ├── three.vue │ └── two.vue │ └── welcome │ └── index.vue └── static └── favicon.ico /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ // 预设 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { // 需要支持哪些平台+哪些版本 7 | // 针对浏览器版本的配置 8 | "browsers" : ["> 1%", "ie >= 9", "chrome >= 60", "ios >= 9", "android >= 4.0", "last 2 versions"] 9 | 10 | }, 11 | // 按需引入 减少打包后的JS文件 12 | "useBuiltIns": "usage" 13 | } 14 | ] 15 | ], 16 | "plugins": [ // 插件 17 | [ 18 | "component", 19 | { 20 | "libraryName": "element-ui", 21 | "styleLibraryName": "theme-chalk" 22 | } 23 | ] 24 | ] 25 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | .sass-cache/ 4 | npm-debug.log.* 5 | node_tmp/ 6 | .vscode 7 | Thumbs.db 8 | .DS_Store 9 | .svn 10 | package-lock.json 11 | .DS_Store 12 | */.DS_Store 13 | .DS_Store 14 | dist/ 15 | -------------------------------------------------------------------------------- /READEME.md: -------------------------------------------------------------------------------- 1 | ## 一.概念 2 | 3 | webpack 是一个模块打包器(module bundler)。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。 4 | 5 | ### 1.1.入口(entry) 6 | 7 | 指示webpack应该使用哪个模块来作为构建其内部依赖图的入口起点。(默认值为**./src**) 8 | 9 | ### 1.2.出口(output) 10 | 11 | 这个属性告诉webpack在哪里输出它所创建的 bundles,以及如何命名这些文件。(默认值为**./dist**) 12 | 13 | 1. 通过output.filename: 告诉webpack bundle的名称; 14 | 2. 通过output.path:告诉webpack bundle生成(emit)到哪里。 15 | 16 | ### 1.3.加载器(loader) 17 | 18 | 让webpack能够去处理那些非js文件(webpack自身只能处理js),loader可以将所有类型的文件转换为webpack能够处理的模块(modules)。 19 | 20 | 1. test属性:用于标识出应该被对应的loader进行转换的某个或者某些文件; 21 | 2. use属性:用于表示进行转换时,应该使用哪个loader。 22 | 23 | ### 1.4.插件(plugins) 24 | 25 | loader被用于转换某些类型的模块,而插件可以用于执行范围更广的任务,从打包优化到压缩,插件目的在于解决loader无法实现的其他事情。使用步骤为: 26 | 27 | 1. 先require它,然后把它添加到plugins数组中; 28 | 2. 可以在配置文件中因为不同目的而多次使用同一个插件,通过 new 来创建一个它的实例。 29 | 30 | ### 1.5.模式(mode) 31 | 32 | 通过选择development和production之中的一个,来设置mode参数,你可以启用相应模式下的webpack内置的优化。 33 | 34 | ## 二.项目实战 35 | 36 | ### 2.1.初始化项目 37 | 38 | ```js 39 | npm init -y 40 | ``` 41 | 42 | 执行上述命令后会生成一个package.json文件,接着安装webpack4 43 | 44 | ### 2.2.安装webpack4 45 | 46 | npm i -D webpack webpack-cli 47 | 48 | 如果你使用的是webpack4+版本,你还需要安装CLI。-D是指开发环境需要,上线不需要。 49 | 50 | ### 2.3.创建目录 51 | 52 | 1. 创建src 53 | 54 | 这个里面放的是源代码,“源”代码是用于书写和编辑的代码。 55 | 56 | 2. 创建dist,并且在该文件夹下创建index.html 57 | 58 | 这个里面放的是分发代码,“分发”代码是构建过程产生的压缩后的“输出”目录,最终将在浏览器中加载; 59 | 60 | index.html页面script引入的标签由原始的./src文件改为加载最终打包后的bundle,main.js; 61 | 62 | 执行 webpack 命令,会将我们的脚步作为入口起点,然后输出 main.js。 63 | 64 | 3. 配置webpack.config.js 65 | 66 | 添加配置文件比在终端中手动输入大量命令要高效的多,所以让我们创建一个取代之前使用CLI选项方式的配置文件。 67 | 68 | ```diff 69 | webpack-demo 70 | |- package.json 71 | + |- webpack.config.js 72 | |- /dist 73 | |- index.html 74 | |- /src 75 | |- index.js 76 | ``` 77 | 78 | ​ 考虑到用CLI这种方式来运行本地的webpack很不方便,我们可以通过创建一个npm脚本的方式来设置快捷方式。 79 | 80 | ​ 81 | 82 | ```diff 83 | "scripts": { 84 | "test": "echo \"Error: no test specified\" && exit 1", 85 | + "build": "webpack" 86 | }, 87 | ``` 88 | 89 | ​ 通过运行 npm run build 命令就可以代替我们之前使用的webpack命令。并且通过向该命令和你的参数见添加两个中横线,可以将自定义参数传递给webpack,npm run build -- --colors。 90 | 91 | ### 2.4.管理资源 92 | 93 | ​ 1.加载css 94 | 95 | ```js 96 | cnpm i -D style-loader css-loader 97 | ``` 98 | 99 | 安装上述两个模块,style-loader依赖于css-loader,loader的书写规则为从右到左形成一种依赖关系,最先处理的放在最右边。 100 | 101 | ​ 2.加载图片 102 | 103 | ```js 104 | cnpm i -D file-loader 105 | ``` 106 | 107 | 可以看到实际的文件名已更改为诸如hash模式那样前缀的类型,这意味着webpack在src文件中找到我们的图片文件,并成功处理过它,下一步紧接着的就是压缩和优化你的图像。 108 | 109 | ```js 110 | cnpm i -D url-loader 111 | ``` 112 | 113 | 提高文件的限制大小,之前是224kib,配置如何展示性能提示。例如,如果一个资源超过250kb,webpack会对此输出一个警告来通知你,performance属性。 114 | 115 | ​ 3.加载字体 116 | 117 | file-loader和url-loader可以接收并加载任何文件,然后将其输出到构建目录,也就是说,我们可以将它们用于任何类型的文件,包括字体。 118 | 119 | ### 2.5.管理输出 120 | 121 | 到目前为止,我们所做的一切都是在index.html文件中手动引入所有资源,然而随着应用程序的增长,使用了多个bundle的时候,需要手动地对index.html文件进行管理,一切就会变得困难起来。可以通过下载一些插件,让我们不用手动更改index.html文件。 122 | 123 | ​ 1.为你自动生成一个HTML文件,其中包括使用script标签的body中的所有webpack包。 124 | 125 | ```js 126 | cnpm i -D html-webpack-plugin 127 | ``` 128 | 129 | 由于过去的代码示例遗留下来,导致/dist文件夹相当杂乱,包含了之前的示例代码。webpack会生成文件,然后将这些文件放置在、dist文件夹中,但是webpack无法追踪到哪些文件是实际在项目中所需要使用到的。 130 | 131 | ​ 2.通常,在每次构建前清理/dist文件夹,是比较推荐的做法,因此只会生成用到的文件。 132 | 133 | ```js 134 | cnpm i -D clean-webpack-plugin 135 | ``` 136 | 137 | 现在,你不会看到旧的文件,只有构建后生成的文件。 138 | 139 | ​ 3.Manifest,webpack及其插件似乎“知道”应该哪些文件生成,答案是,通过manifest,webpack能够对【你的模块映射到输出bundle的过程】保持追踪。 140 | 141 | ### 2.6.正式开发 142 | 143 | ​ 1.追踪错误和警告在源代码中的原始位置。 144 | 145 | For development, use `cheap-module-eval-source-map`. For production, use `cheap-module-source-map`. 146 | 147 | ​ devtool: 'cheap-module-eval-source-map', // 这是 "cheap(低开销)" 的 source map。 148 | 149 | ​ 2.每次要编译代码时,手动运行 npm run build 就会变得很麻烦。 150 | 151 | ```js 152 | cnpm i -D webpack-dev-server // 提供了一个简单的web服务器,并且能够实时重新加载(live reloading) 在代码发生变化后自动编译代码 153 | ``` 154 | 155 | ​ 修改配置文件,告诉开发服务器(dev server),在哪里查找文件:告知webpack-dev-server,在localhost:8080下建立服务,将dist目录下的文件,作为可访问文件。 156 | 157 | ```diff 158 | + devServer: { 159 | + contentBase: './dist' 160 | + }, 161 | ``` 162 | 163 | ​ Please update `webpack-cli` to v4 and use `webpack serve` to run webpack-dev-server 164 | 165 | ### 2.7.模块热替换 166 | 167 | 模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。 168 | 169 | ### 2.8.tree shaking 170 | 171 | Tree Shaking指的就是当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码,这就需要借助webpack里面自带的Tree Shaking这个功能,帮助我们实现。 172 | 173 | 压缩输出:从 webpack 4 开始,也可以通过 `"mode"` 配置选项轻松切换到压缩输出,只需设置为 `"production"`。 174 | 175 | ```diff 176 | + mode: "production" 177 | ``` 178 | 179 | ## 三、生产环境 180 | 181 | ### 3.1.生产环境构建 182 | 183 | 开发环境(development):需要强大的、具有实时重新加载(live reloading)或热模块(hot module replacement)能力的source map和localhost server。 184 | 185 | 生产环境(production):我们的目标倾向于关注更小的bundle,更轻量的source map,以及更优化的资源,以改善加载时间。 186 | 187 | 由于这两个环境的构建目标差异很大,并且要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的webpack配置。 188 | 189 | ​ 1.我们还是会遵循不重复原则(Don't repeat yourself - DRY),保留一个“通用”配置。为了将这些配置合并在一起,我们使用“通用”配置。 190 | 191 | ```js 192 | cnpm i -D webpack-merge 193 | ``` 194 | 195 | ​ 2.使用tree shaking 196 | 197 | ```js 198 | cnpm i -D uglifyjs-webpack-plugin 199 | ``` 200 | 201 | ```diff 202 | + const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 203 | ``` 204 | 205 | `UglifyJs` does not support ES6。可以使用 UglifyEs 206 | 207 | ​ 3.配置scripts 208 | 209 | - npm start 定义为开发环境脚本 210 | 211 | - npm run build 定义为生产环境脚本 212 | 213 | 4.指定环境 214 | 215 | 许多库library将通过与环境变量关联,以决定库中应该应用哪些内容。例如,当处于开发环境中,某些库为了使调试变得容易,可能会添加额外的日志记录和测试。 216 | 217 | ```diff 218 | + new webpack.DefinePlugin({ 219 | + 'process.env.NODE_ENV': JSON.stringify('production') 220 | + }) 221 | ``` 222 | 223 | *技术上讲,*`NODE_ENV` *是一个由 Node.js 暴露给执行脚本的系统环境变量。通常用于决定在开发环境与生产环境(dev-vs-prod)下,服务器工具、构建脚本和客户端 library 的行为。* 224 | 225 | ### 3.2.代码分离 226 | 227 | bundle分析输出结果。一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户: 228 | 229 | ```js 230 | cnpm i -D webpack-bundle-analyzer 231 | ``` 232 | 233 | 通过使用 `output.filename` 进行[文件名替换](https://www.webpackjs.com/configuration/output#output-filename),可以确保浏览器获取到修改后的文件。`[hash]` 替换可以用于在文件名中包含一个构建相关(build-specific)的 hash,但是更好的方式是使用 `[chunkhash]` 替换,在文件名中包含一个 chunk 相关(chunk-specific)的哈希。输出文件的文件名: 234 | 235 | ```diff 236 | + filename: '[name].[chunkhash].js', 237 | ``` 238 | 239 | ### 3.3.shimming 240 | 241 | webpack编译器能够识别遵循ES2015模块的语法、CommonJS或AMD规范编写的模块,然而,一些第三方的库(library)可能会引用一些全局依赖(例如jQuery中的$)。这些库也可能创建一些需要被导出的全局变量。这些“不合符规范的模块”就是shimming发挥作用的地方。 242 | 243 | ​ 1.使用 ProvidePlugin 后,能够在通过webpack编译的每个模块中,通过访问一个变量来获取package包。(如果webpack知道这个变量在某个模块中被使用了,那么webpack将在最终bundle中引入我们给定的package) 自动加载模块,而不必到处 import或require 244 | 245 | ```diff 246 | + plugins: [ 247 | + new webpack.ProvidePlugin({ 248 | + _: 'lodash' 249 | + }) 250 | + ] 251 | ``` 252 | 253 | shim是一个库,它将一个新的API引入到旧的环境中,并且仅依靠旧环境中已有的手段实现。polyfil就是一个用在浏览器API上的shim。我们通常的做法是先检查当前浏览器是否支持某个API,如果不支持的话就加载对应的polyfill。然后新旧浏览器就都可以使用这个API了。 254 | 255 | 3.2.渐进式网络应用程序(PWA) 256 | 257 | Progressive Web Application 是一种可以提供类似于原生应用程序体验的网络应用程序。PWA可以用来在离线时能够继续运行功能。 258 | 259 | ​ 1.搭建一个简易服务器,真正的用户是通过网络访问网络应用程序;用户的浏览器会与一个提供所需资源(例如 html、js和css文件)的服务器通讯。 260 | 261 | ```js 262 | cnpm i -D http-server 263 | ``` 264 | 265 | ### 3.4.使用环境变量 266 | 267 | 要在开发和生产构建之间,消除webpack.config.js的差异,你可能需要环境变量。 268 | 269 | 直接在命令行环境配置中,通过设置--env并传入尽可能多的环境变量。 270 | 271 | ```bash 272 | webpack --env.NODE_ENV=local --env.production --progress 273 | ``` 274 | 275 | ### 3.5.配置process.env 276 | 277 | 通常情况下,我们需要针对不同环境(开发环境、集成环境、生产环境等),进行相应策略的配置(比如是否替换接口地址,代码是否进行压缩等)。webpack就是通过process.env这个属性进行区别的。它是Node.js提供的一个API,它返回的是一个包含用户环境信息的独一性。如果我们给Node.js设置一个环境变量·,并把它挂载到process.env返回的对象上,便可以在代码中进行相应环境的判断。 278 | 279 | 通常的做法是,新建一个环境变量NODE_ENV,用它确定当前所处的开发阶段,生产阶段设为production,开发阶段设为development或staging,然后在脚本中读取process.env.NODE_ENV即可。要说明的是,NODE_ENV 这个名称只是开发社区的一种共识,名称内容是可以修改的。 280 | 281 | 由于Windows和Mac系统有区别,这就会到值我在Windows上开发部署的项目,到了Mac系统下就无法使用了,反之依然。为了解决这个问题,引入cross-env。它是一个跨平台设置环境变量的第三方包,它可以让你一行命令,就能轻松地在多个平台设置环境变量。 282 | 283 | ```js 284 | cnpm i -D cross-env 285 | ``` 286 | 287 | 然后package.json文件中配置就可以了。在项目中可以根据这个值来区分当前环境。 288 | 289 | ## 四、项目报错处理 290 | 291 | 1. Error: Cannot find module 'webpack-cli/bin/config-yargs' 292 | 293 | 报错前安装的版本为: 294 | 295 | ```js 296 | "webpack": "4.4.1", 297 | "webpack-cli": "^4.1.0", 298 | "webpack-dev-server": "^3.11.0", 299 | ``` 300 | 301 | 原因为webpack新版本并不与现有版本兼容(webpack-cli几天前发布的最新版4.0.0版移除了yargs包,而紧挨4.0.0版本的上一正式版本3.3.12还在:)去寻找适合webpack4的webpack-cli和webpack-dev-server 302 | 303 | ```js 304 | "webpack": "4.4.1", 305 | "webpack-cli": "^3.3.2", 306 | "webpack-dev-server": "^3.11.0", 307 | ``` 308 | 309 | 新版本尤其是大的版本更新都改变了很多,很多都从架构上进行了更改,老版本的功能包比如webpack-dev-server就不能兼容新版本的webpack了。那么我们是不是就不能使用最新版本的webpack了?其实不是,想用就要么等兼容版本,要么有精力自己搞一个兼容版本,再或者可以搜索相关功能适合最新webpack的解决方案。 310 | 311 | ## 五、插件详解 312 | 313 | ### 5.1.预处理器postcss-loader 314 | 315 | 用postcss处理css的loader,利用js代码来处理css,负责把css代码解析成抽象语法树结构,再交由插件来进行处理,插件基于css代码的AST所能进行的操作是多种多样的,比如增加浏览器相关的声明前缀。 316 | 317 | ### 5.2.后处理程序autoPrefixer 318 | 319 | 是一个流行的PostCss插件,其作用是为css中的属性添加浏览器特定的前缀,开发人员在编写css时只需要使用css规范中的标准属性名即可。 320 | 321 | ``` 322 | #content { 323 | display: flex; 324 | } 325 | ``` 326 | 327 | 在经过 Autoprefixer 处理之后得到的css: 328 | 329 | ``` 330 | #content { 331 | display: -webkit-box; 332 | display: -webkit-flex; 333 | display: -ms-flexbox; 334 | display: flex; 335 | } 336 | ``` 337 | 338 | ### 5.3.拆分css 339 | 340 | webpack4.0以前,我们通过extract-text-webpack-plugin插件,把css样式从js文件中提取到单独的css文件中。webpack4.0以后,官方推荐使用mini-css-extract-plugin插件来打包css文件,会将所有的css样式合并为一个css文件。 341 | 342 | 配置文件如下: 343 | 344 | ```js 345 | module: { 346 | rules: [ 347 | // 这个插件应该只在生产环境构建中使用,并且在loader链中不应该有style-loader,特别是我们在开发模式中使用HMR时 348 | { 349 | test: /\.css$/, 350 | use: [ MiniCssExtractPlugin.loader,'css-loader','postcss-loader' ] 351 | } 352 | ], 353 | }, 354 | ``` 355 | 356 | ### 5.4.用babel转译js文件 357 | 358 | 为了使我们的js代码能够兼容更多的环境我们需要安装依赖 359 | 360 | 1. babel-loader:加载ES2015+代码,然后使用Babel(一个js编译器,让你立即使用下一代的js)转译为ES5,是一个npm包,它使得webpack可以通过babel转译js代码。 361 | 362 | ```json 363 | 注释:(在 babel 7 中 `babel-core` 和 `babel-preset` 被建议使用 `@babel` 开头声明作用域,因此应该分别下载 `@babel/core` 和`@babel/presets`。就类似于 vue-cli 升级后 使用@vue/cli一样的道理 )Babel的功能在于【代码转译】,具体一点,就是将目标代码转译为能够符合期望语法规范的代码。在转译的过程中,babel内部经历了【解析-转换-生成】三个步骤。而`@babel/core`这个库则负责【解析】,具体的【转换】和【生成】步骤则交给各种插件(plugin)和预设(preset)来完成。 364 | ``` 365 | 366 | 2. @babel/core:如果你需要以编程的方式来使用Babel,可以使用这个包。 367 | 368 | ```json 369 | 注释:(`@babel/core`的作用是把js代码分析成ast树,方便各个插件分析语法进行相应的处理。有些新语法在低版本js中是不存在的,比如箭头函数,reset函数,函数默认值等,这种语言层面的不兼容只能通过将代码转为ast,分析其语法后再转为低版本的js) 370 | ``` 371 | 372 | 3. @babel/preset-env:智能预设,是一系列插件的集合,可以根据配置的目标浏览器或者运行环境来按需加载插件。 373 | 374 | ```json 375 | 注释:(实际上就是各种插件的打包组合,包含了我们在babel7中常用的es2015,es2016, es2017等最新的语法转化插件,允许我们使用最新的js语法,比如let、const、箭头函数等等,但不包括stage-x阶段的插件。也就是说各种转译规则的统一设定,目的是告诉loader要以什么规则来转化成对应的js版本) 376 | ``` 377 | 378 | `babel-loader`与`babel-core`的版本对应关系 379 | 380 | 1. `babel-loader` 8.x 对应`babel-core` 7.x 381 | 2. `babel-loader` 7.x 对应`babel-core` 6.x 382 | 383 | 配置文件如下: 384 | 385 | ```js 386 | module: { 387 | // 创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式,这些规则能够对模块(module)应用loader,或者修改解析器(parser)。 388 | rules: [ 389 | { 390 | test: /\.js$/, 391 | exclude: /node_modules/, // 确保转译尽可能少的文件 392 | use: ['babel-loader?cacheDirectory'] // 将babel-loader提速至少两倍,将转译的结果缓存到文件系统中 将使用默认的缓存目录(node_modules/.cache/babel-loader) 393 | }, 394 | { test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'] }, // 解析图片 395 | { test: /.(woff|woff2|eot|ttf|otf)$/, use: ['url-loader'] }, // 解析字体 396 | ] 397 | }, 398 | ``` 399 | 400 | 你需要创建一个.babelrc文件,让它实际执行操作: 401 | 402 | ```json 403 | { 404 | "presets": ["@babel/preset-env"] 405 | } 406 | ``` 407 | 408 | 上面的babel-loader只会将ES6/7/8语法转换为ES5语法,但是对新的api并不会转换,例如(Promise、Generator、Set、Maps、Proxy等),此时我们需要借助babel-polyfill来帮助我们转换: 409 | 410 | ```js 411 | cnpm i -S @babel/polyfill 412 | ``` 413 | 414 | 因为polyfill要在编译你的源代码之前执行,所以要安装为dependency而不是devDependency,解决低版本浏览器(比如IE)不兼容问题。 415 | 416 | ## 六、搭建vue环境 417 | 418 | ### 6.1.加载和转译Vue组件 419 | 420 | 1.vue-loader 是webpack的一个预处理器,它允许你以一种成为单文件组件single-file-components(SFCs)的格式来编写Vue组件。 421 | 422 | ```js 423 | cnpm i -D vue-loader 424 | ``` 425 | 426 | 前提得先安装vue这个框架: 427 | 428 | ```js 429 | cnpm i -S vue 430 | ``` 431 | 432 | ```json 433 | -D:--save-dev 生产环境的包 434 | -S:--save 开发环境的包 435 | ``` 436 | 437 | 安装编译.vue组件里的template部分的编译器 438 | 439 | ```js 440 | cnpm i -D vue-template-compiler 441 | ``` 442 | 443 | 2.需要注意的是vue-template-compiler编译器的版本必须和基本的 `vue` 包保持同步,这样 `vue-loader` 就会生成兼容运行时的代码。 444 | 445 | 需要注意的是,vue有不同的构建版本。基于构建工具时使用 **ES** **Module**,为打包工具提供的ESM,ESM格式被设计为可以被静态分析,所以打包工具可以利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包,为这些打包工具提供的默认文件是只有运行时的ES Module构建【vue.runtime.esm.js】 446 | 447 | 在webpack中的配置: 448 | 449 | ```json 450 | // webpack.config.js 451 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 452 | 453 | module.exports = { 454 | module: { 455 | rules: [ 456 | // ... 其它规则 457 | { 458 | test: /\.vue$/, 459 | loader: 'vue-loader' 460 | } 461 | ] 462 | }, 463 | plugins: [ 464 | // 请确保引入这个插件! 465 | new VueLoaderPlugin() 466 | ] 467 | } 468 | ``` 469 | 470 | **这个插件是必须的!** 它的职责是将你定义过的其它规则复制并应用到 `.vue` 文件里相应语言的块。例如,如果你有一条匹配 `/\.js$/` 的规则,那么它会应用到 `.vue` 文件里的 ` 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --env.dev --config build/webpack.dev.js", 8 | "build": "cross-env NODE_ENV=production webpack --env.prod --mode=production --config build/webpack.prod.js", 9 | "http": "http-server dist" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "@babel/core": "^7.12.9", 16 | "@babel/preset-env": "^7.12.7", 17 | "autoprefixer": "^10.0.2", 18 | "babel-loader": "^8.2.2", 19 | "babel-plugin-component": "^1.1.1", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "cross-env": "^7.0.2", 22 | "css-loader": "^5.0.0", 23 | "file-loader": "^6.1.1", 24 | "html-webpack-plugin": "^4.5.0", 25 | "http-server": "^0.12.3", 26 | "mini-css-extract-plugin": "^1.3.1", 27 | "postcss-loader": "^4.0.4", 28 | "sass": "^1.32.4", 29 | "sass-loader": "^10.1.1", 30 | "style-loader": "^2.0.0", 31 | "uglifyjs-webpack-plugin": "^2.2.0", 32 | "url-loader": "^4.1.1", 33 | "vue-loader": "^15.9.6", 34 | "vue-template-compiler": "^2.6.12", 35 | "webpack": "^4.4.1", 36 | "webpack-bundle-analyzer": "^3.9.0", 37 | "webpack-cli": "^3.3.2", 38 | "webpack-dev-server": "^3.11.0", 39 | "webpack-merge": "^4.2.1" 40 | }, 41 | "dependencies": { 42 | "@babel/polyfill": "^7.12.1", 43 | "axios": "^0.21.1", 44 | "element-ui": "^2.15.0", 45 | "mockjs": "^1.1.0", 46 | "vue": "^2.6.12", 47 | "vue-router": "^3.5.1", 48 | "vuex": "^3.6.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins:[ 3 | require("autoprefixer") 4 | ] 5 | } -------------------------------------------------------------------------------- /proce.js: -------------------------------------------------------------------------------- 1 | console.log(process); -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/api/api.js: -------------------------------------------------------------------------------- 1 | const API = { 2 | API_POST_LOGIN: '/login', 3 | API_GET_LIST: '/list/get', 4 | API_GET_TOTAL: '/list/total', 5 | API_DEL_LIST: '/list/delete', 6 | API_ADD_LIST: '/list/add', 7 | API_EDIT_LIST: '/list/edit', 8 | API_SEARCH_LIST: '/list/search', 9 | } 10 | 11 | export { 12 | API, 13 | API as default 14 | } -------------------------------------------------------------------------------- /src/api/http.js: -------------------------------------------------------------------------------- 1 | import Axios from 'axios' 2 | 3 | // 请求的基本配置 起别名 4 | // 使用别名方法时,不需要在配置中指定url、method和data属性 5 | // axios.request(config) 6 | const request = (url, params, method = 'get') => { 7 | let options = { 8 | method: method 9 | } 10 | options.data = params 11 | return Axios({url, ...options}) 12 | } 13 | 14 | export const get = request 15 | 16 | export const post = (url, params) => request(url, params, 'post') 17 | 18 | export const del = (url, params) => request(url, params, 'delete') 19 | 20 | export const put = (url, params) => request(url, params, 'put') 21 | -------------------------------------------------------------------------------- /src/api/interface.js: -------------------------------------------------------------------------------- 1 | import { get, post, put, del } from '@/api/http.js' 2 | import API from '@/api/api.js' 3 | 4 | const INTERFACE = { 5 | // 登录 6 | login:(params) => post(API.API_POST_LOGIN, params), 7 | 8 | // 获取列表 9 | getList:(params) => get(API.API_GET_LIST, params), 10 | 11 | // 获取列表总数 12 | getTotal:() => get(API.API_GET_TOTAL), 13 | 14 | // 删除 15 | deleteList:(params) => del(API.API_DEL_LIST, params), 16 | 17 | // 新增 18 | addList:(params) => post(API.API_ADD_LIST, params), 19 | 20 | // 编辑 21 | editList:(params) => put(API.API_EDIT_LIST, params), 22 | 23 | // 搜索 24 | searchList:(params) => get(API.API_SEARCH_LIST, params) 25 | } 26 | 27 | export { 28 | INTERFACE, 29 | INTERFACE as default 30 | } -------------------------------------------------------------------------------- /src/assets/css/index.scss: -------------------------------------------------------------------------------- 1 | // Normalize.css 只是一个很小的CSS文件,但它在默认的HTML元素样式上提供了跨浏览器的高度一致性。相比于传统的CSS reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案。 2 | @import './modules/normalize.scss'; 3 | // 重置掉一些样式 4 | @import './modules/reset.scss'; 5 | // 颜色变量等配置样式 6 | @import './modules/config.scss'; 7 | // 基础公用的css文件 8 | @import './modules/common.scss'; 9 | // 重写原有样式的文件 10 | @import './modules/reset.scss'; -------------------------------------------------------------------------------- /src/assets/css/modules/common.scss: -------------------------------------------------------------------------------- 1 | html,body { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | // 加入通用css代码(引入一次就行) symbol方式引入的 6 | .icon { 7 | // width: 1em; 8 | height: 1em; 9 | vertical-align: -0.15em; 10 | fill: currentColor; 11 | overflow: hidden; 12 | // font-size: 16px; 13 | } 14 | 15 | .page { 16 | height: 100%; 17 | display: flex; 18 | flex-flow: column nowrap; 19 | background-color: #f0f3f4; 20 | .page-filter { 21 | padding: 20px 20px 0px; 22 | background-color: #fff; 23 | border-top: 1px solid #dcdfe6; 24 | border-bottom: 1px solid #dcdfe6; 25 | .left { 26 | float: left; 27 | } 28 | .right { 29 | float: right; 30 | } 31 | } 32 | .page-content { 33 | height: 100%; 34 | flex: 1; 35 | margin: 20px; 36 | overflow: hidden; 37 | background-color: #fff; 38 | .table-wrapper { 39 | height: 100%; 40 | display: flex; 41 | flex-flow: column nowrap; 42 | .table-box { 43 | flex: 1; 44 | overflow: hidden; 45 | } 46 | .pagination-box { 47 | height: 32px; 48 | margin: 5px auto; 49 | } 50 | } 51 | } 52 | .page-dialog { 53 | .el-dialog__wrapper.w-600 .el-dialog { 54 | width: 600px; 55 | } 56 | .el-dialog__header { 57 | padding: 20px 20px 0px; 58 | } 59 | .el-dialog__body { 60 | padding: 20px; 61 | } 62 | .el-dialog__footer { 63 | text-align: center; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/assets/css/modules/config.scss: -------------------------------------------------------------------------------- 1 | $--color-primary: #23B7E5; 2 | $--color-success: #27C24C; 3 | $--color-danger: #fa5c7c; -------------------------------------------------------------------------------- /src/assets/css/modules/normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } -------------------------------------------------------------------------------- /src/assets/css/modules/reset.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | html,body { 4 | -webkit-text-size-adjust: 100%; 5 | -ms-text-size-adjust: 100%; 6 | } 7 | 8 | body { 9 | height: 100%; 10 | -moz-osx-font-smoothing: grayscale; 11 | -webkit-font-smoothing: antialiased; 12 | text-rendering: optimizeLegibility; 13 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 14 | } 15 | 16 | *, 17 | *:before, 18 | *:after { 19 | -webkit-box-sizing: border-box; 20 | -moz-box-sizing: border-box; 21 | box-sizing: border-box; 22 | } 23 | 24 | // unify the setting of elements's margin and padding for browsers 25 | article,aside,blockquote,body,button,code,dd,details,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,input,legend,li,menu,nav,ol,p,pre,section,td,textarea,th,ul { 26 | margin: 0; 27 | padding: 0 28 | } 29 | 30 | // Reset fonts for relevant elements 31 | button,input,select,textarea { 32 | font-family: inherit; 33 | font-size: inherit; 34 | line-height: inherit; 35 | color: inherit; 36 | } 37 | 38 | ol,ul { 39 | list-style: none 40 | } -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1611740222622'); /* IE9 */ 3 | src: url('iconfont.eot?t=1611740222622#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAARMAAsAAAAACIQAAAP9AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqEOIQAATYCJAMQCwoABCAFhG0HTxt5B1GUDkqK7ENJQTAwQnJYkAEDYAAkAAAREA/RGr83u3P396thSTRpNbVEKSRIiVCtZLKHRLr5flMvrcesYnQmFWMVcWRiND0IjT5yEzEnTFS+9se90/8bXmDzWZbLXMuiLiqNtwa253mXRWFcIAl6D3EbvLr0OoExG3QhO1JSRYAgSZ8XiDc8x4CghEZWEEOT9wVbU3zSaeVDuY6P3vfjn10RRNFV+tILz4qbQd5P3Tuujh5Gu+MguMOZwO6iYicgiReFlkd8g8hOvsbk0X72AZqmMNSA+R03DMjbiqQZf3kUoRI9XTKB+D3iemBci07hp5lO8JPTqIDN2Q2j+EwEwa+7f0i5RatUT62JVCnnDKM7EaJoliRzhwXeaqIjfcSbSrSlLygUCy7hZHvq0P1c54RtNq+XXGAURUKSqIUh9++TISvN9J9CS1L3i1KrdfdLL3hjR+8+fjnu0K19a09evHw6cvSKLQeGLT3ZTqKkmo3nO1wnRGL98VDgfdlGdH/CEfKpUsNdqZVsUUgytXHRNNMd0jQG03L2nz/VDM4x26aZEoTRNPFaW8eE0IumbRmNz/l/7HRiccXUsXeT0W7N7+v98lLLK79X9Ums6Weu6XCyFVrUaoAqtZs9cVr0znnRe7OIrmdxy/AM63gwbF0o+7YcH4fjyPSaF8owucMkmTQ5GRlMp3GSLEqa+4Ig1GpL/MJ7s+irW8mHoGD6jadPLrcBpn/fXADfy0d2O1BqvLzIWBg+bVQD07fIkB9eErDvhvLbxDr/3X0L+CTGXAkaUilE7r64+zxmxQdOzR0W/KVnqgLKoupC3jW2xB8n19eXmyoUc5vf++pvW9ZuGTR4SWQyjQyeEnHu7M2bi9ckRUwhVuCme+rX0gtnfS/bGMQ6pnb5D/zG63cBr+9PZ/1tigQ/cDIHXIOYoJkUfEhuY+4X5zasSYWvazMlgcVRrel8LEXBmKHnOyeY82wI2WLjXEKzGEExYj1UzUY0udvIpTNuD0Cv2Q9jdijdPW6ewFfkKLabQCDM2AXFlC9QzbiBJv4pdJZ8hd5M+MKYa2E4ctzm4OWFAmQQbMaahmC8xeVkTT2S+/kNkBtgZ4Q43yNRUPDwfiwxLiEdL4VOKAyxwjOQS0KIxVjB5cBKyG3QbndhbsHVAi0ozoqQOyM+ni16UJzF5QByhwkgBgI1wzQZAsOzcHFindaIPPP5BhBnADuGUFFVZqRAAg9+eEyiOAkN0FKJs1HVpYz2GIiTBEFYahZL4OKAKQETZDciLhh38aQWkAUSx9oh5JYhHuuxTSVx08scd7gNxuhHZJSokdEZez6AcTbxDqOCUQzmGafHGo2XD2GcQ63QyTkYAAAAAAA=') format('woff2'), 5 | url('iconfont.woff?t=1611740222622') format('woff'), 6 | url('iconfont.ttf?t=1611740222622') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1611740222622#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-guanbimima:before { 19 | content: "\e616"; 20 | } 21 | 22 | .icon-xianshimima:before { 23 | content: "\e611"; 24 | } 25 | 26 | .icon-yanzhengma:before { 27 | content: "\e667"; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(t){var e,n,i,o,c,l,a='',d=(d=document.getElementsByTagName("script"))[d.length-1].getAttribute("data-injectcss");if(d&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}function s(){c||(c=!0,i())}e=function(){var t,e,n,i;(i=document.createElement("div")).innerHTML=a,a=null,(n=i.getElementsByTagName("svg")[0])&&(n.setAttribute("aria-hidden","true"),n.style.position="absolute",n.style.width=0,n.style.height=0,n.style.overflow="hidden",t=n,(e=document.body).firstChild?(i=t,(n=e.firstChild).parentNode.insertBefore(i,n)):e.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),e()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(i=e,o=t.document,c=!1,(l=function(){try{o.documentElement.doScroll("left")}catch(t){return void setTimeout(l,50)}s()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,s())})}(window); -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/images/aside/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/aside/1.jpg -------------------------------------------------------------------------------- /src/assets/images/aside/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/aside/2.jpg -------------------------------------------------------------------------------- /src/assets/images/login/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/login/1.jpg -------------------------------------------------------------------------------- /src/assets/images/login/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/login/2.jpg -------------------------------------------------------------------------------- /src/assets/images/login/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/login/3.jpg -------------------------------------------------------------------------------- /src/assets/images/login/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/src/assets/images/login/4.jpg -------------------------------------------------------------------------------- /src/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /src/components/ImgVerify/index.vue: -------------------------------------------------------------------------------- 1 | //* 图形验证码组件 2 | 11 | 12 | 147 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础组件的自动化全局注册 3 | * 注:文件夹的名称即为components在页面的名称 4 | * 列:uploadImage ==> 5 | */ 6 | import Vue from 'vue' 7 | /** 8 | * 有三个参数 9 | * 参数1:其组件目录的相对路径 10 | * 参数2:是否查询其子目录 11 | * 参数3:匹配基础组件文件名的正则 12 | */ 13 | const requireComponent = require.context('../components', true, /\.vue$/) 14 | 15 | requireComponent.keys().forEach(fileName => { 16 | // 获取组件配置 17 | const componentConfig = requireComponent(fileName) 18 | // 获取组件名称 19 | const componentName = fileName.split('/')[1].replace('.vue','') 20 | // 全局注册组件 21 | Vue.component( 22 | componentName, 23 | // 如果这个组件选项是通过 `export default` 导出的,那么就会优先使用 `.default`, 24 | // 否则回退到使用模块的根。 25 | componentConfig.default || componentConfig 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // 引入vue 2 | import Vue from 'vue' 3 | // 引入vue-router 4 | import VueRouter from 'vue-router' 5 | // console.log('❤️❤️',Vue); 6 | import Hello from '@/components/Hello.vue' 7 | import App from '@/App.vue' 8 | 9 | import '@/assets/css/index.scss' 10 | 11 | // 全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生 12 | import '@/components/index.js' 13 | 14 | // 引入elementUi 15 | import ElementUI from 'element-ui' 16 | import 'element-ui/lib/theme-chalk/index.css' 17 | 18 | Vue.use(ElementUI) 19 | Vue.use(VueRouter) 20 | 21 | import router from '@/router/index.js' 22 | 23 | // 引入iconfont 24 | // import '@/assets/iconfont/iconfont.js' 25 | 26 | // 引入mock模拟本地数据 27 | import '@/mock/index.js' 28 | 29 | // 引入vuex 30 | import store from '@/store/index.js' 31 | 32 | // 引入layout 33 | // import layout from '@/views/layout/index.vue' 34 | new Vue({ 35 | el: '#app', 36 | router, 37 | store, 38 | // 不需要编译器 39 | render: h => h(App), 40 | // 需要编译器 (挂载到一个元素上并以其 DOM 内部的 HTML 作为模板)) 41 | // template: '' 42 | }).$mount('#app') -------------------------------------------------------------------------------- /src/mock/index.js: -------------------------------------------------------------------------------- 1 | // 使用 Mock 2 | const Mock = require('mockjs') 3 | 4 | // 指定被拦截的Ajax请求的响应时间, 单位是毫秒 5 | Mock.setup({ 6 | timeout: '500-1000' 7 | }) 8 | 9 | // 获取Random对象 10 | const Random = Mock.Random 11 | 12 | // 为 Mock.Random 扩展方法 13 | Random.extend({ 14 | likes: function() { 15 | const likes = [ 16 | '浴室。每次洗完身子感觉整个身体都在那里放松了。', 17 | '哈哈哈,我喜欢的是和女孩子一起玩,因为男女搭配,干活不累嘛。', 18 | '我没啥爱好,唯一的爱好就是宅。', 19 | '我喜欢看电影,每个周末有时间了就会美滋滋得享受一部好看的电影。', 20 | '我喜欢美食,因为我觉得人无非就三样,吃喝拉撒睡,首当其冲的便是吃,只有吃好了每天才过的开心。', 21 | '死宅。一般喜欢宅在家里,不太喜欢出去活动。在家里折腾电脑手机,写写程序,看看编程技术和科技方面的书,几乎每一个程序员手边都有一堆这样那样的书。程序员常常即使没有问题也会去找一些问题来自己瞎折腾,这成了一种乐趣。', 22 | '看b站。沙雕的生活区,馋人的美食区,可爱的萌宠区,养眼的舞蹈区。', 23 | '泡网。喜欢泡网,因为网上可以发现很多新奇的东西,学习很多新奇的编程技术,看到很多新奇的IT新闻。程序员往往喜欢比较前言的东西,这些东西也都是最先在网上出现。久而久之,程序员不喜欢网络都不行。不管是遇到问题,还是查询信息,都是首先定性的想到网络了。', 24 | '我最最喜欢的就是去旅游了,看沿途的风景,真是美呆了。', 25 | '玩手机。手机是现在很多人喜欢把玩的电子产品,只有到外面走一走就知道了,坐公共交通,等交通工具,等人,甚至上班、开会、走路都有人拿着手机在玩,当然程序员也不例外。当然程序员跟非程序员玩手机的目的还是有一点差别,大多数玩手机是聊qq,玩游戏,什么植物大战僵尸、保卫萝卜、史密斯奶奶、滑雪、神庙、碰碰消和各种各样的手机网游都有人玩,但据我了解,程序员总体上并不是很喜欢玩游戏,往往QQ群、微信群,IT新闻或是一些搞笑搞怪的东西关心的更多。', 26 | ] 27 | return this.pick(likes) 28 | } 29 | }) 30 | Mock.Random.extend({ 31 | address: function () { 32 | const address = [ 33 | '浙江省嘉兴市沈荡古镇', 34 | '四川省成都市大熊猫基地', 35 | '深圳市南山区白石洲中信红树湾', 36 | '日本大阪环球影城', 37 | '四川成都市中德英伦联邦C区', 38 | '北京市中南海老四合院靠左', 39 | '安徽省安庆市宿松县许岭镇', 40 | '上海市浦东新区咪咕视频', 41 | '上海市浦东新区金桥经济开发区金港路766号', 42 | '上海市浦东新区唐镇小湾村', 43 | ] 44 | return this.pick(address) 45 | } 46 | }) 47 | 48 | const list = [] 49 | for (let i = 0; i < 20; i++) { 50 | list.push({ 51 | id: i + 1, 52 | date: Random.date(), 53 | name: Random.cname(), 54 | address: Random.address(), 55 | likes: Random.likes() 56 | }) 57 | } 58 | 59 | // 用户信息 60 | const users = [ 61 | { 62 | id: 1, 63 | username: 'admin', 64 | userpic: 'https://i.loli.net/2021/03/12/83l5mSiFdwY7VNr.jpg', 65 | password: 'admin', 66 | token: 'abcdefghijklmnopqrstuvwxyz', 67 | description: '超级管理员,具备所有页面的权限', 68 | rolename: 'ADMIN', 69 | rights: [ 70 | { 71 | id: 11, 72 | authName: '一级菜单', 73 | icon: 'el-icon-connection', 74 | children: [ 75 | { 76 | id: 111, 77 | authName: '一级项目1', 78 | icon: 'el-icon-s-grid', 79 | path: '/menu/one', 80 | rights: ['view', 'edit', 'add', 'delete'] 81 | }, 82 | { 83 | id: 112, 84 | authName: '一级项目2', 85 | icon: 'el-icon-s-marketing', 86 | path: '/menu/two', 87 | rights: ['view', 'edit', 'add', 'delete'] 88 | } 89 | ] 90 | }, 91 | { 92 | id: 22, 93 | authName: '二级菜单', 94 | icon: 'el-icon-set-up', 95 | children: [ 96 | { 97 | id: 221, 98 | authName: '二级项目1', 99 | icon: 'el-icon-s-custom', 100 | path: '/menu/three', 101 | rights: ['view', 'edit', 'add', 'delete'] 102 | }, 103 | { 104 | id: 222, 105 | authName: '二级项目2', 106 | icon: 'el-icon-s-custom', 107 | path: '/menu/four', 108 | rights: ['view', 'edit', 'add', 'delete'] 109 | }, 110 | { 111 | id: 223, 112 | authName: '二级项目2', 113 | icon: 'el-icon-s-custom', 114 | path: '/menu/five', 115 | rights: ['view', 'edit', 'add', 'delete'] 116 | }, 117 | ] 118 | }, 119 | ], 120 | }, 121 | { 122 | id: 2, 123 | username: 'visitor', 124 | userpic: 'https://i.loli.net/2021/03/12/6g5Htir3YwBySCe.png', 125 | password: 'visitor', 126 | token: 'abcdefghijklmnopqrstuvwxyz'.split('').reverse().join(''), 127 | description: '游客,仅仅具备查看所有页面的权限', 128 | rolename: 'VISITOR', 129 | rights: [ 130 | { 131 | id: 11, 132 | authName: '一级菜单', 133 | icon: 'el-icon-connection', 134 | children: [ 135 | { 136 | id: 111, 137 | authName: '一级项目1', 138 | icon: 'el-icon-s-grid', 139 | path: '/menu/one', 140 | rights: ['view'] 141 | }, 142 | { 143 | id: 112, 144 | authName: '一级项目2', 145 | icon: 'el-icon-s-marketing', 146 | path: '/menu/two', 147 | rights: ['view', 'edit', 'add', 'delete'] 148 | } 149 | ] 150 | }, 151 | ], 152 | }, 153 | ] 154 | 155 | /** 156 | * Mock.mock( rurl, rtype, function( options ) ) 157 | * rurl 需要拦截的url 158 | * rtype 需要拦截的ajax请求类型 159 | * function( options ) 用于生成响应数据的函数 160 | * 记录用于生成响应数据的函数。当拦截到匹配 rurl 和 rtype 的 Ajax 请求时,函数 function(options) 将被执行,并把执行结果作为响应数据返回。 161 | */ 162 | 163 | // 用户登录 164 | Mock.mock('/login', 'post', options => { 165 | const {userName, userPassword} = JSON.parse(options.body) 166 | const user = users.find(item => { 167 | return item.username === userName && item.password === userPassword 168 | }) 169 | 170 | return user 171 | }) 172 | 173 | // 获取列表 174 | Mock.mock(/\/list\/get[\s\S]*?/, 'get', options => { 175 | const { pageNo, pageSize } = JSON.parse(options.body) 176 | // slice 截取数组(start,end) 并不会修改数组,而是返回一个子数组 177 | 178 | return list.slice(((pageNo - 1) * pageSize), pageNo * pageSize) 179 | }) 180 | 181 | // 获取列表总数 182 | Mock.mock(/\/list\/total/, 'get', () => { 183 | return list.length 184 | }) 185 | 186 | // 删除 187 | Mock.mock(/\/list\/delete[\s\S]*?/, 'delete', options => { 188 | const { id } = JSON.parse(options.body) 189 | const index = list.findIndex(item => item.id === id) 190 | const item = index > 0 ? list[index] : {} 191 | list.splice(index, 1) 192 | 193 | return item 194 | }) 195 | 196 | // 新增 197 | Mock.mock('/list/add', 'post', options => { 198 | const item = JSON.parse(options.body) 199 | item.id = list[list.length - 1].id + 1 200 | item.date = new Date().toLocaleDateString().replace(/\//g, '-') 201 | list.unshift(item) 202 | // 把新增的这一项 作为返回值返回给res.data 203 | return item 204 | }) 205 | 206 | // 编辑 207 | Mock.mock('/list/edit', 'put', options => { 208 | const item = JSON.parse(options.body) 209 | let itemTemp = {} 210 | list.forEach((i, index) => { 211 | if (i.id === item.id) { 212 | itemTemp = item 213 | list[index] = item 214 | } 215 | }) 216 | 217 | return itemTemp 218 | }) 219 | 220 | // 查询 221 | Mock.mock(/\/list\/search[\s\S]*?/, 'get', options => { 222 | const filter = JSON.parse(options.body) 223 | // 多条件筛选 224 | // 拿到有值的参数 多个参数中 只要有一个满足条件 就返回该条件的查询结果 225 | let tempFilter = {} 226 | for (let key in filter) { 227 | if (filter[key]) { 228 | tempFilter[key] = filter[key] 229 | } 230 | } 231 | 232 | let tempList = list.filter(item => { 233 | for (let k in tempFilter) { 234 | if (item[k].includes(tempFilter[k])) { 235 | return true 236 | } else { 237 | return false 238 | } 239 | } 240 | }) 241 | 242 | return { list: tempList, total: tempList.length } 243 | }) -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import VueRouter from 'vue-router' 2 | 3 | const router = new VueRouter({ 4 | routes: [ 5 | { 6 | path: '/', 7 | redirect: '/welcome', 8 | }, 9 | { 10 | path: '', 11 | component: () => import('@/views/layout/index.vue'), 12 | children: [ 13 | { 14 | path: '/menu/one', 15 | component: () => import('@/views/pages/menu/one.vue'), 16 | }, 17 | { 18 | path: '/menu/two', 19 | component: () => import('@/views/pages/menu/two.vue'), 20 | }, 21 | { 22 | path: '/menu/three', 23 | component: () => import('@/views/pages/menu/three.vue'), 24 | }, 25 | { 26 | path: '/menu/four', 27 | component: () => import('@/views/pages/menu/four.vue'), 28 | }, 29 | { 30 | path: '/menu/five', 31 | component: () => import('@/views/pages/menu/five.vue'), 32 | }, 33 | { 34 | path: '/welcome', 35 | component: () => import('@/views/pages/welcome/index.vue'), 36 | }, 37 | { 38 | path: '/login', 39 | component: () => import('@/views/pages/login/index.vue'), 40 | meta: { 41 | hideBar: true 42 | }, 43 | }, 44 | ] 45 | }, 46 | // { 47 | // path : "*", // 用户输入的URL路由地址是没有的 给个默认的地址 48 | // redirect: '/welcome' 49 | // }, 50 | ] 51 | }) 52 | 53 | router.beforeEach((to, from, next) => { 54 | if (sessionStorage.getItem('login') === 'yes') { 55 | next() 56 | } else { 57 | if (to.path == '/login') { 58 | next() 59 | } else { 60 | next({ path: '/login' }) 61 | } 62 | } 63 | }) 64 | 65 | // 解决 重复点击路由出现的控制台报错 禁止全局的路由错误处理 66 | const originalPush = VueRouter.prototype.push 67 | VueRouter.prototype.push = function push (location) { 68 | return originalPush.call(this, location).catch(err => err) 69 | } 70 | 71 | export default router -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | const store = new Vuex.Store({ 7 | state: { 8 | $user: JSON.parse(localStorage.getItem('$user')) || {}, 9 | $isCollapse: false, 10 | }, 11 | mutations: { 12 | setUser(state, data) { 13 | state.$user = data 14 | localStorage.setItem('$user', JSON.stringify(data)) 15 | }, 16 | switchCollapse(state, boolean) { 17 | state.$isCollapse = boolean 18 | }, 19 | }, 20 | actions: {}, 21 | }) 22 | 23 | export default store -------------------------------------------------------------------------------- /src/views/layout/aside.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 46 | 47 | 83 | -------------------------------------------------------------------------------- /src/views/layout/header.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 58 | 59 | 93 | -------------------------------------------------------------------------------- /src/views/layout/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 39 | 40 | 62 | -------------------------------------------------------------------------------- /src/views/pages/login/index.vue: -------------------------------------------------------------------------------- 1 | //*登录组件 2 | 46 | 47 | 105 | 106 | 142 | -------------------------------------------------------------------------------- /src/views/pages/menu/five.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/views/pages/menu/four.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/views/pages/menu/one.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 258 | 259 | 265 | -------------------------------------------------------------------------------- /src/views/pages/menu/three.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/views/pages/menu/two.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/views/pages/welcome/index.vue: -------------------------------------------------------------------------------- 1 | //*欢迎组件 2 | 7 | 8 | 16 | 25 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shiwanjun1995/webpack-basic/a6eb92ac5641aedc3c9e44c6a131edbde9d5f379/static/favicon.ico --------------------------------------------------------------------------------