安装vs-code和插件
EditorConfig for VS Code ESLint gitignore language-stylus Nunjucks One Dark Pro PostCSS syntax Vetur View In Browser vscode-icons
软件配置
{ "window.zoomLevel": 2, "editor.fontSize": 20, "workbench.iconTheme": "vscode-icons", "files.autoSave": "onFocusChange", "terminal.integrated.fontSize": 20, "editor.tabSize": 2 }
打开命令行:ctrl+~
初始化项目
npm init
安装
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的)
安装警告依赖
npm i css-loader vue-template-compiler
初始化好了
新建src/app.vue
<template> <div id="test">{{text}}</div> </template> <script> export default { data(){ return{ text:'abc' } } } </script> <style> #test{color: red} </style>
添加脚本
index.js
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
"build": "webpack --config webpack.config.js"
执行打包命令
npm run build
生成dist文件夹
红色报错,说明没有编译器解释
npm run build
npm i style-loader url-loader file-loader
url-loader是建立在file-loader基础上的,base64,。
limit对文件大小做限制。
use不仅是读取,还包括做的一些处理。
[name]原文件名,[ext]扩展名
如运行还有其他报错,可参考上图给安装依赖版本
npm run build
新建styles/test-stylus.styl样式文件,写法很随意,可以不要大括号和冒号等
npm run build
需要安装stylus-loader
npm i stylus-loader stylus
安装
npm i webpack-dev-server@^2.9.7
这个版本应该与webpack版本相互兼容,3.1.5版本会报错,推测要在3以下
针对不同平台的依赖
npm i cross-env
说明:mac上不需要用set,windows上需要
配置好dev
引入html的插件
安装
npm i html-webpack-plugin
配置
使用插件,在js中可以直接引用环境判断,vue可以根据不同环境打包,开发环境会有很多错误提示,但是正式环境不需要
npm run dev
打开查看
设置热加载
package.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" },
webpack.config.js
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
安装插件
npm i postcss-loader autoprefixer babel-loader babel-core
项目文件夹下创建babelrc和postcss.config.js配置文件
postcss是在css文件都编译完之后,通过autoprefixer对其优化,需要加入浏览器前缀支持的
将vue-jsx文件的转化
npm i babel-preset-env babel-plugin-transform-vue-jsx
安装依赖
编译处理
更快一些
npm i babel-helper-vue-jsx-merge-props@^2.0.0 css-loader@^0.28.7 babel-plugin-syntax-jsx npm run dev
打包没报错即可
调整assets文件夹到src下
删除之前测试使用的
新建src/todo及其以下文件
新建assets/styles/globel.styl文件
引入
样式内容略,删除app.vue的内容
样式具体内容略。lang指定预处理器,scoped仅仅对本组件作用
(1)基本组件的引用,样式
header.vue
<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
<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
<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
<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
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
#footer background:rgba(0,0,0,0.2) text-align: center margin-top: 50px
global.styl
body background-image: url('../images/bg.jpg')
父子组件通信
(2)数量统计,tab切换面板,clear清楚所选item
至此代码:
tabs.vue
<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
<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
<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>
npm i extract-text-webpack-plugin
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
npm run build
打包生成文件名
附带:package.json
{ "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": {} }
如有版本报错,可根据上面替换安装
让浏览器加载更快
npm run build
hash和chunkhash区别:
chunkhash是每个模块一个hash,hash会有区别,而hash是整个应用一个hash
webpack.config.js
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
npm install vue-loader@^13.7.2
2.You may need an appropriate loader to handle this file type
解决:可能有些包的版本不合适,需要更改
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句