Vue+webpack实战 TodoList(附项目源码)

251 阅读2分钟

全局安装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

源码

me.csdn.net/download/fu… 

download.csdn.net/download/fu…

创作打卡挑战赛

赢取流量/现金/CSDN周边激励大奖