全局安装vue-cli
npm i vue-cli -g
创建项目
vue init webpack-simple vue-todos
进入项目文件夹,项目初始化
cd vue-todos
npm i
运行项目,将会看到默认界面
npm run dev
这里将app.vue中其余内容删除,只保留基本框架
<template>
<!--视图模板-->
<!--注意!从vue2.0开始组件模板必须有且只有一个顶层元素-->
<div id="app">
</div>
</template>
<!--组件定义-->
<script>
export default {
name: 'app'
}
</script>
<!--组件样式表-->
<style>
</style>
这里样式使用less,需进行相关配置
npm i less style-loader css-loader less-loader -D
在webpack.config.js中进行相应配置
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
},
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader']
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
数据绑定和样式绑定
App.vue
<template>
<!--视图模板-->
<!--注意!从vue2.0开始组件模板必须有且只有一个顶层元素-->
<div id="app">
<h1>{{title}}</h1>
<ul class="todos">
<li>
<input v-model="newTodo"
@keyup.13="addItem"
placeholder="写下了要记住的事情!"
autofocus="true"/>
</li>
<li v-for="(todo,index) in todos"
:class="{'checked':todo.done}">
<input type="checkbox"
@change="saveToStore"
v-model="todo.done"/>
<label>{{index+1}}.{{todo.value}}</label>
<time>{{todo.created|date}}</time>
<button @click.prevent="delItem(todo)"></button>
</li>
</ul>
</div>
</template>
<!--组件定义-->
<script>
import './assets/todos.less'//引入less样式表
import './assets/site.less'
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('zh-cn')
export default {
name: 'app',
data(){
return {
title:"vue-todos",
todos:[
{
value:"学Vue",
done:false,
created:Date.now()
},
{
value:"学JavaScript",
done:true,
created:Date.now()-30000000
},
{
value:"学Node",
done:false,
created:Date.now()
}
]
}
},
created(){
if(this.is_initialized){
this.todos = JSON.parse(localStorage.getItem('VUE-TODOS'))
}
},
computed:{
is_initialized(){
return localStorage.getItem('VUE-TODOS')!=null
}
},
filters:{
date(val){
return moment(val).calendar()
}
},
methods:{
addItem(){
this.todos.push({
value:this.newTodo,
created:Date.now(),
done:false
});
this.saveToStore();
this.newTodo=''
},
delItem(todo){
this.todos = this.todos.filter((x)=>x!==todo)
this.saveToStore()
},
saveToStore(){
localStorage.setItem('VUE-TODOS',JSON.stringify(this.todos))
}
}
}
</script>
<!--组件样式表-->
<style>
</style>
在src/assets文件夹下新建样式文件
site.less
html,
body {
margin: 0;
padding: 0;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
font-smoothing: antialiased;
font-weight: 100;
min-width: 230px;
max-width: 550px;
}
h1 {
width: 100%;
font-size: 80px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
button,
input {
outline: none;
}
.hidden {
display: none;
}
@media (min-width: 768px) {
body {
max-width: auto;
}
}
todos.less
@input_color: #e6e6e6;
@input_font_weight:300;
@checkbox_img:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>';
@checkbox_checked_img:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>';
.todos {
margin: 0;
list-style-type: none;
padding: 0 20px;
border-top: 1px solid #ccc;
background: #fff;
border: 1px solid #ccc;
box-shadow: 0 10px 30px #ccc;
position: relative;
transiton: .3s;
& input::-webkit-input-placeholder {
font-style: italic;
font-weight: @input_font_weight;
color: @input_color;
}
& input::-moz-placeholder {
font-style: italic;
font-weight: @input_font_weight;
color: @input_color;
}
& input::-moz-placeholder {
font-style: italic;
font-weight: @input_font_weight;
color: @input_color;
}
& input::-ms-input-placeholder {
font-style: italic;
font-weight: @input_font_weight;
color: @input_color;
}
& > li {
cursor: pointer;
font-size: 24px;
line-height: 36px;
padding: 12px 0;
border-bottom: 1px solid #ededed;
position: relative;
&:last-child {
border-bottom: none;
}
&:first-child > input {
font-size: 24px;
padding: 10px;
border: none;
width: 100%;
background: transparent;
}
& > input[type=checkbox] {
display: inline-block;
vertical-align: middle;
text-align: center;
width: 40px;
height: auto;
position: absolute;
top: 10px;
bottom: 0;
margin: auto 0;
border: none;
-webkit-appearance: none;
appearance: none;
&:after {
content: url(@checkbox_img);
}
}
& > time{
position: absolute;
right: 60px;
top: 15px;
font-size: 9pt;
}
& > label {
display: block;
vertical-align: middle;
padding-left: 50px;
letter-spacing: 2;
}
& > button {
position: absolute;
right: 5px;
top: 15px;
height: 30px;
width: 30px;
border: none;
background: none;
display: none;
&:after, &:before {
content: "";
position: absolute;
top: 15px;
left: 0;
transform: rotateZ(45deg);
height: 1px;
width: 30px;
background: #cc9a9a;
}
&:before {
transform: rotateZ(-45deg);
}
}
&.checked {
& > input[type=checkbox] {
&:after {
content: url(@checkbox_checked_img);
}
}
& > label {
color: #d9d9d9;
text-decoration: line-through;
}
}
&:hover {
& > button {
display: block;
}
}
}
}
运行项目
npm run dev