前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue+Webpack打造todo应用 原

Vue+Webpack打造todo应用 原

作者头像
晓歌
发布2018-08-15 11:50:18
1.3K0
发布2018-08-15 11:50:18
举报
文章被收录于专栏:破晓之歌

一、课程介绍

课程地址:https://www.imooc.com/learn/935

1.概要

2.vue-loader+webpack项目配置

安装vs-code和插件

代码语言:javascript
复制
EditorConfig for VS Code
ESLint
gitignore
language-stylus
Nunjucks
One Dark Pro
PostCSS syntax
Vetur
View In Browser
vscode-icons

软件配置

代码语言:javascript
复制
{
    "window.zoomLevel": 2,
    "editor.fontSize": 20,
    "workbench.iconTheme": "vscode-icons",
    "files.autoSave": "onFocusChange",
    "terminal.integrated.fontSize": 20,
    "editor.tabSize": 2
}

打开命令行:ctrl+~

初始化项目

代码语言:javascript
复制
npm init

安装

代码语言:javascript
复制
npm i webpack@^3.10.0 vue vue-loader@^13.6.0

注意:

A.webpack需要安装到4版本以下

B.vue-loader需要安装15版本以下(参考官方文档 https://vue-loader.vuejs.org/migrating.html#a-plugin-is-now-required . Vue-loader在15.*之后的版本都是 vue-loader的使用都是需要伴生 VueLoaderPlugin的)

安装警告依赖

代码语言:javascript
复制
npm i css-loader vue-template-compiler

初始化好了

新建src/app.vue

代码语言:javascript
复制
<template>
    <div id="test">{{text}}</div>
</template>
<script>
export default {
    data(){
        return{
            text:'abc'
        }
    }
}
</script>
<style>
#test{color: red}
</style>

添加脚本

index.js

代码语言:javascript
复制
import Vue from 'vue'
import App from './app.vue'

const root = document.createElement('div')

document.body.appendChild(root)

new Vue({
  render:(h) => h(App)
}).$mount(root)

 webpack.config.js

代码语言:javascript
复制
"build": "webpack --config webpack.config.js"

执行打包命令 

代码语言:javascript
复制
npm run build

生成dist文件夹

红色报错,说明没有编译器解释

代码语言:javascript
复制
npm run build

3. webpack配置项目加载各种静态资源及css预处理器

代码语言:javascript
复制
npm i style-loader url-loader file-loader

url-loader是建立在file-loader基础上的,base64,。

limit对文件大小做限制。

use不仅是读取,还包括做的一些处理。

[name]原文件名,[ext]扩展名

如运行还有其他报错,可参考上图给安装依赖版本

代码语言:javascript
复制
npm run build

新建styles/test-stylus.styl样式文件,写法很随意,可以不要大括号和冒号等

代码语言:javascript
复制
npm run build

需要安装stylus-loader

代码语言:javascript
复制
npm i stylus-loader stylus

4.webpack-dev-server的配置使用

安装

代码语言:javascript
复制
npm i webpack-dev-server@^2.9.7

这个版本应该与webpack版本相互兼容,3.1.5版本会报错,推测要在3以下

针对不同平台的依赖

代码语言:javascript
复制
npm i cross-env

说明:mac上不需要用set,windows上需要

配置好dev

引入html的插件

安装

代码语言:javascript
复制
npm i html-webpack-plugin

配置

使用插件,在js中可以直接引用环境判断,vue可以根据不同环境打包,开发环境会有很多错误提示,但是正式环境不需要

代码语言:javascript
复制
npm run dev

打开查看

设置热加载

package.js(部分)

代码语言:javascript
复制
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
  },

webpack.config.js

代码语言:javascript
复制
const path = require('path')

const HTMLPlugin = require('html-webpack-plugin')
const webpack = require('webpack')

const isDev = process.env.NODE_ENV === 'development'

const config = {
  target: 'web',
  entry:path.join(__dirname,'src/index.js'),
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.styl/,
        use: [
          'style-loader',
          'css-loader',
          'stylus-loader'
        ]
      },

      {
        test: /\.(gif|jpg|jepg|png|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 1024,
              name: '[name]-aaa.[ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: isDev ? '"development"' : '"production"'
      }
    }),
    new HTMLPlugin()
  ]
}

if (isDev) {
  config.devtool = '#cheap-module-eval-source-map' // 浏览器打开后,通过映射以编译后我们能看懂方式调整,source-map最完整映射关系,但是编译效率比较低,文件比较大,eval可能看起来会比较乱,出现行对应不齐的问题。而推荐的这个效率比较高
  config.devServer = {
    port: 8000,
    host: '0.0.0.0',
    overlay: {
      errors: true,
    },
    hot: true // 改了一个组件的代码,只重新渲染这个组件,不贵整个页面渲染
    // historyFallback: {

    // }// 入口地址映射,(略)
    // open: true //启动后自动打开页面
  },
  config.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin() // 不需要信息展示的问题
  )
}

module.exports = config

二、vue2介绍和项目实践

1.vue2的核心知识介绍

2.配置vue的jsx写法以及postcss

安装插件

代码语言:javascript
复制
npm i postcss-loader autoprefixer babel-loader babel-core

项目文件夹下创建babelrc和postcss.config.js配置文件

postcss是在css文件都编译完之后,通过autoprefixer对其优化,需要加入浏览器前缀支持的

将vue-jsx文件的转化

代码语言:javascript
复制
npm i babel-preset-env babel-plugin-transform-vue-jsx

安装依赖

编译处理

更快一些

代码语言:javascript
复制
npm i babel-helper-vue-jsx-merge-props@^2.0.0 css-loader@^0.28.7 babel-plugin-syntax-jsx
npm run dev

打包没报错即可

3. 实现todo应用的界面

调整assets文件夹到src下

删除之前测试使用的

新建src/todo及其以下文件

新建assets/styles/globel.styl文件

引入

样式内容略,删除app.vue的内容

样式具体内容略。lang指定预处理器,scoped仅仅对本组件作用

(1)基本组件的引用,样式

header.vue

代码语言:javascript
复制
<template>
  <header class="main-header">
    <h1>Todo</h1>
  </header>
</template>
<style lang = 'stylus' scoped>
.main-header{
  text-align center
  h1{
    font-size 100px
    color rgba(175,47,47,0.6)
    font-weight 500
    margin 20px
  }
}
</style>

todo.vue

代码语言:javascript
复制
<template>
  <section class="real-app">
    <input 
      type="text"
      class="add-input"
      autofocus='autofocus'
      placeholder="接下来要做什么"
      @keyup.enter="addTodo"
    >
    <item :todo='todo'></item>
    <!-- @keyup 也就等于 v-on:keyup -->
    <tabs :filter = 'filter'></tabs>
  </section>
</template>
<script>
import Item from './item.vue'
import Tabs from './tabs.vue'
export default {
  data() {
    return{
      todo:{
        id:0,
        content:'this is todo',
        completed: false
      },
      filter:'all'
    }
  },
  methods: {
    addTodo(){

    }
  },
  components: {
    Item,Tabs
  }
}
</script>
<style lang=stylus scoped>
.real-app{
  width 70%
  min-width 500px
  padding 10px
  background #ffffff
  color #555555
  margin 20px auto
  box-shadow 0 0 10px 5px rgba(0,0,0,0.5)
  .add-input{
    width 84%
    font-size 24px
    border none
    margin 0 6%
    padding 20px 2%
    outline none
    border-bottom 1px solid #fff
  }
  .add-input:hover{
    color #333
    border-bottom 1px solid #ccc
    cursor pointer
  }
}
</style>

tabs.vue

代码语言:javascript
复制
<template>
  <div class="helper">
    <span class="left">2 items left</span>
    <span class="tabs">
      <span
        v-for="state in states"
        :key="state"
        :class="[state, filter === state ? 'actived' : '']"
        @click="toggleFilter(state)"
      >
        {{state}}
      </span>
    </span>
    <span class="clear" @click="clearAllCompleted" type='button'>
      Clear Completed
    </span>
  </div>
</template>

<script>
export default {
  props: {
    filter: {
      type: String,
      required: true
    }
  },
  data(){
    return{
      states:['all', 'active','completed']
    }
  },
  methods: {
    clearAllCompleted (){},
    toggleFilter(){}
  }
}
</script>
<style lang="stylus" scoped>
.helper{
  text-align center
  .left{width:18%;display inline-block}
  .tabs{
    width 50%
    display inline-block
    margin 10px auto
    span{
      min-width 33%
      display inline-block 
      }
    span:hover{
      cursor pointer
      color orange
    }
    span.actived{
      color red
      }
    }
  .clear{ 
    width 30%
    display inline-block 
    color:grey
    border none
    outline none
    }
  .clear:hover{
    color black
    cursor pointer
    }
  input[type='button']{
    border-width 0
  }
}
</style>

item.vue

代码语言:javascript
复制
<template>
  <div :class="['todo-item',todo.completed ? 'completed' : '']">
    <input 
      type="checkbox"
      class="toggle"
      v-model="todo.completed"
    >
    <label>{{todo.content}}</label>
    <button class="destory" @click="deleteTodo"></button>
  </div>
</template>
<script>
export default {
  props:{
    todo:{
      type:Object,
      required:true
    }
  },
  methods:{
    deleteTodo(){

    }
  }
}
</script>

<style lang=stylus scoped>
.todo-item{
  vertical-align text-top
  border none
  padding 15px 2%
  width 88%
  margin 10px 4%
  border-bottom 1px solid #eee
  font-size 28px
  input[type="checkbox"],input[type="checkbox"]:checked{
    vertical-align text-top
    width 30px
    height 30px
  }
  .destory{
    width 30px
    height 30px
    float  right
  }
}
</style>

footer.jsx

代码语言:javascript
复制
import '../assets/styles/footer.styl'
export default {
  data() {
    return {
      author: 'Jhon'
    }
  },
  render(){
    return (
      <div id="footer">
        <span>written by {this.author}</span>
      </div>
    )
  }
}

footer.styl

代码语言:javascript
复制
#footer
  background:rgba(0,0,0,0.2)
  text-align: center
  margin-top: 50px

global.styl

代码语言:javascript
复制
body
  background-image: url('../images/bg.jpg')

4.实现todo应用的业务逻辑

(1)添加和删除操作

父子组件通信

(2)数量统计,tab切换面板,clear清楚所选item

至此代码:

tabs.vue

代码语言:javascript
复制
<template>
  <div class="helper">
    <span class="left">{{unFinishedTodoLength}} items left</span>
    <span class="tabs">
      <span
        v-for="state in states"
        :key="state"
        :class="[state, filter === state ? 'actived' : '']"
        @click="toggleFilter(state)"
      >
        {{state}}
      </span>
    </span>
    <span class="clear" @click="clearAllCompleted">
      Clear Completed
    </span>
  </div>
</template>

<script>
export default {
  props: {
    filter: {
      type: String,
      required: true
    },
    todos: {
      type: Array,
      required: true
    }
  },
  data(){
    return{
      states:['all', 'active','completed']
    }
  },
  methods: {
    clearAllCompleted (){
      this.$emit('clearAllcompleted')
    },
    toggleFilter(state){
      this.$emit('toggle', state)
    }
  },
  computed: {
    unFinishedTodoLength(){
      return this.todos.filter(todo => !todo.completed).length
    }
  }
}
</script>
<style lang="stylus" scoped>
.helper{
  text-align center
  .left{width:18%;display inline-block}
  .tabs{
    width 50%
    display inline-block
    margin 10px auto
    span{
      min-width 33%
      display inline-block 
      }
    span:hover{
      cursor pointer
      color orange
    }
    span.actived{
      color red
      }
    }
  .clear{ 
    width 30%
    display inline-block 
    color:grey
    border none
    outline none
    }
  .clear:hover{
    color black
    cursor pointer
    }
  input[type='button']{
    border-width 0
  }
}
</style>

todo.vue

代码语言:javascript
复制
<template>
  <section class="real-app">
    <input 
      type="text"
      class="add-input"
      autofocus='autofocus'
      placeholder="接下来要做什么"
      @keyup.enter="addTodo"
    >
    <item 
      :todo='todo'
      v-for="todo in filterdTodos"
      :key="todo.id"
      @del="deleteTodo"
    />
    <!-- @keyup 也就等于 v-on:keyup -->
    <tabs 
      :filter = 'filter' 
      :todos="todos"
      @toggle='toggleFilter'
      @clearAllcompleted="clearAllcompleted"
    
    />
  </section>
</template>
<script>
import Item from './item.vue'
import Tabs from './tabs.vue'
let id = 0
export default {
  data() {
    return{
      todos:[],
      filter:'all'
    }
  },
  methods: {
    addTodo(e){
      this.todos.unshift({
        id: id++,
        content: e.target.value.trim(),
        completed: false
      })
      e.target.value =''
    },
    deleteTodo(id){
      this.todos.splice(this.todos.findIndex(todo => todo.id === id), 1)
    },
    toggleFilter(state){
      this.filter = state
    },
    clearAllcompleted(){
      this.todos = this.todos.filter(todo => !todo.completed)
    }
  },
  computed: {
    filterdTodos(){
      if (this.filter === 'all'){
        return this.todos
      }
      // 判断
      const completed = this.filter === 'completed'
      return this.todos.filter(todo => completed === todo.completed)
    }
  },
  components: {
    Item,Tabs
  }
}
</script>
<style lang=stylus scoped>
.real-app{
  width 70%
  min-width 500px
  padding 10px
  background #ffffff
  color #555555
  margin 20px auto
  box-shadow 0 0 10px 5px rgba(0,0,0,0.5)
  .add-input{
    width 84%
    font-size 24px
    border none
    margin 0 6%
    padding 20px 2%
    outline none
    border-bottom 1px solid #fff
  }
  .add-input:hover{
    color #333
    border-bottom 1px solid #ccc
    cursor pointer
  }
}
</style>

item.vue 

代码语言:javascript
复制
<template>
  <div :class="['todo-item',todo.completed ? 'completed' : '']">
    <input 
      type="checkbox"
      class="toggle"
      v-model="todo.completed"
    >
    <label>{{todo.content}}</label>
    <button class="destory" @click="deleteTodo"></button>
  </div>
</template>
<script>
export default {
  props:{
    todo:{
      type:Object,
      required:true
    }
  },
  methods:{
    deleteTodo(){
      this.$emit('del', this.todo.id)
    }
  }
}
</script>

<style lang=stylus scoped>
.todo-item{
  vertical-align text-top
  border none
  padding 15px 2%
  width 88%
  margin 10px 4%
  border-bottom 1px solid #eee
  font-size 28px
  input[type="checkbox"],input[type="checkbox"]:checked{
    vertical-align text-top
    width 30px
    height 30px
  }
  .destory{
    width 30px
    height 30px
    float  right
  }
}
</style>

三、webpack配置优化

1. 配置css单独分离打包

代码语言:javascript
复制
npm i extract-text-webpack-plugin

代码语言:javascript
复制
const path = require('path')

const HTMLPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const ExtractPlugin = require('extract-text-webpack-plugin')
const isDev = process.env.NODE_ENV === 'development'

const config = {
  target: 'web',
  entry:path.join(__dirname,'src/index.js'),
  output: {
    filename: 'bundle[hash:8].js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.jsx/,
        loader: 'babel-loader'
      },
      {
        test: /\.(gif|jpg|jepg|png|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 1024,
              name: '[name]-aaa.[ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: isDev ? '"development"' : '"production"'
      }
    }),
    new HTMLPlugin()
  ]
}

if (isDev) {
  config.module.rules.push({
      test: /\.styl/,
      use: [
        'style-loader',
        'css-loader',
        {
          loader: 'postcss-loader',
          options: {
            sourceMap: true,
          }
        },
        'stylus-loader'
      ]
  })
  config.devtool = '#cheap-module-eval-source-map' // 浏览器打开后,通过映射以编译后我们能看懂方式调整,source-map最完整映射关系,但是编译效率比较低,文件比较大,eval可能看起来会比较乱,出现行对应不齐的问题。而推荐的这个效率比较高
  config.devServer = {
    port: 8000,
    host: '0.0.0.0',
    overlay: {
      errors: true,
    },
    hot: true // 改了一个组件的代码,只重新渲染这个组件,不贵整个页面渲染
    // historyFallback: {

    // }// 入口地址映射,(略)
    // open: true //启动后自动打开页面
  },
  config.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin() // 不需要信息展示的问题
  )
} else{
  config.output.filename = '[name][chunkhash:8].js'
  config.module.rules.push(
    {
      test: /\.styl/,
      use: ExtractPlugin.extract({
        fallback: 'style-loader',
        use: [
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          },
          'stylus-loader'
        ]
      })
    }  
  ),
  config.plugins.push(
    new ExtractPlugin('styles.[contentHash:8].css')
  )
}

module.exports = config

代码语言:javascript
复制
npm run build

打包生成文件名

附带:package.json

代码语言:javascript
复制
{
  "name": "vue-ssr-tech",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "autoprefixer": "^7.2.3",
    "babel-core": "^6.26.3",
    "babel-helper-vue-jsx-merge-props": "^2.0.0",
    "babel-loader": "^7.1.2",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-vue-jsx": "^3.7.0",
    "babel-preset-env": "^1.7.0",
    "cross-env": "^5.1.3",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^1.1.11",
    "html-webpack-plugin": "^3.2.0",
    "postcss-loader": "^3.0.0",
    "style-loader": "^0.22.1",
    "stylus": "^0.54.5",
    "stylus-loader": "^3.0.2",
    "url-loader": "^1.0.1",
    "vue": "^2.5.17",
    "vue-loader": "^13.7.2",
    "vue-template-compiler": "^2.5.17",
    "webpack": "^3.10.0",
    "webpack-dev-server": "^2.9.7"
  },
  "devDependencies": {}
}

如有版本报错,可根据上面替换安装

2.区分打包类库代码及hash优化

让浏览器加载更快

代码语言:javascript
复制
npm run build

hash和chunkhash区别:

chunkhash是每个模块一个hash,hash会有区别,而hash是整个应用一个hash

webpack.config.js

代码语言:javascript
复制
const path = require('path')

const HTMLPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const ExtractPlugin = require('extract-text-webpack-plugin')
const isDev = process.env.NODE_ENV === 'development'

const config = {
  target: 'web',
  entry:path.join(__dirname,'src/index.js'),
  output: {
    filename: 'bundle[hash:8].js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.jsx/,
        loader: 'babel-loader'
      },
      {
        test: /\.(gif|jpg|jepg|png|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 1024,
              name: '[name]-aaa.[ext]'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: isDev ? '"development"' : '"production"'
      }
    }),
    new HTMLPlugin()
  ]
}

if (isDev) {
  config.module.rules.push({
      test: /\.styl/,
      use: [
        'style-loader',
        'css-loader',
        {
          loader: 'postcss-loader',
          options: {
            sourceMap: true,
          }
        },
        'stylus-loader'
      ]
  })
  config.devtool = '#cheap-module-eval-source-map' // 浏览器打开后,通过映射以编译后我们能看懂方式调整,source-map最完整映射关系,但是编译效率比较低,文件比较大,eval可能看起来会比较乱,出现行对应不齐的问题。而推荐的这个效率比较高
  config.devServer = {
    port: 8000,
    host: '0.0.0.0',
    overlay: {
      errors: true,
    },
    hot: true // 改了一个组件的代码,只重新渲染这个组件,不贵整个页面渲染
    // historyFallback: {

    // }// 入口地址映射,(略)
    // open: true //启动后自动打开页面
  },
  config.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin() // 不需要信息展示的问题
  )
} else{
  config.entry = {
    app: path.join(__dirname,'src/index.js'),
    vendor: ['vue']
  }
  // config.output.filename = '[name].[hash:8].js'
  config.output.filename = '[name].[chunkhash:8].js'
  config.module.rules.push(
    {
      test: /\.styl/,
      use: ExtractPlugin.extract({
        fallback: 'style-loader',
        use: [
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true
            }
          },
          'stylus-loader'
        ]
      })
    }  
  ),
  config.plugins.push(
    new ExtractPlugin('styles.[contentHash:8].css'),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'verdor'
    }),
    // 在有新的模块加入的时候,webpack是会给新模块加入id的,插入顺序不同,倒是id会变化,使用浏览器的缓存就是去效果,这种方式可以规避。verdor要放在runtime前面
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime'
    })
  )
}

module.exports = config

四、总结

搭建项目-webpack构建(eg:vue-loader,jsx->babel,静态资源,缓存)

webpack中文网:https://www.webpackjs.com/

webapck官网(要访问外国网站):http://webpack.github.io/

配置很多

vue开发todo应用,.vue文件,数据传递,拆分组建,双向绑定,虚拟DOM,jsx文件-复杂场景(vue2)

错误解答

1.No parser and no filepath given

代码语言:javascript
复制
npm install vue-loader@^13.7.2

2.You may need an appropriate loader to handle this file type

解决:可能有些包的版本不合适,需要更改

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、课程介绍
    • 1.概要
      • 2.vue-loader+webpack项目配置
        • 3. webpack配置项目加载各种静态资源及css预处理器
          • 4.webpack-dev-server的配置使用
          • 二、vue2介绍和项目实践
            • 1.vue2的核心知识介绍
              • 2.配置vue的jsx写法以及postcss
                • 3. 实现todo应用的界面
                  • 4.实现todo应用的业务逻辑
                    • (1)添加和删除操作
                • 三、webpack配置优化
                  • 1. 配置css单独分离打包
                    • 2.区分打包类库代码及hash优化
                    • 四、总结
                    • 错误解答
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档