安装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
解决:可能有些包的版本不合适,需要更改