为了提升代码的逼格,之后代码改为Vue文件组件,之前代码虽然读起来容易理解,而且适合在小的项目中使用,但是有如下缺点:
\
文件扩展名为 .vue
的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 Webpack 或 Browserify 等构建工具。
所以基于这种考虑,我们以后会使用单文件模式去编写代码,并且为了看上去更洋气一些,我们的代码会以IView作为基础组件,不熟悉的同学正好可以简单了解一些,本节主要基于单文件组件重构上节的代码。
vue官方提供了很好的命令行工具,vue-cli,可通过npm直接安装 npm install -g vue-cli;对此我不做过多介绍,google到的内容你看都看不过来。
我们基于webpack-simple 脚手架搭建我们的项目,运行 vue init webpack-simple demo,接着一步一步走就ok了,然后进入demo 文件夹,执行npm install 安装依赖即可,安装完毕后执行npm run dev 即可启动程序:
看到上述结果表示已经运行成功,从package.json的script节可以看到,开发模式下启动了热加载模式,无需手动刷新浏览器即可完成代码重载。
既然使用IView,那么我们先安装IView ,npm install --save iview; 并在webpack 入口页面引入并启用
修改我们的webpack.config.js,保证支持css引入以及字体文件导入(npm install --save-dev css-loader style-loader url-loader):
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: /\.vue$/, loader: ‘vue-loader‘, options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: ‘babel-loader‘, exclude: /node_modules/ }, { test: /\.css$/, use: [‘style-loader‘,‘css-loader‘ ] }, { test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/, loader: ‘url-loader?limit=1024‘ }, ] }, resolve: { alias: { ‘vue$‘: ‘vue/dist/vue.esm.js‘ } }, devServer: { historyApiFallback: true, noInfo: 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 }) ]) }
此时我们的iview已经可用了,我们这里引入了iview全部组件,如果按需引入,则需要对每一个组件进行分别引入。首先搭建我们布局页(直接简单修改iview layout代码):
<template> <div class="layout" :class="{‘layout-hide-text‘: spanLeft < 5}"> <Row type="flex"> <i-col :span="spanLeft" class="layout-menu-left"> <Menu active-name="1" theme="dark" width="auto"> <div class="layout-logo-left"> Demo Project </div> <Menu-item name="1"> <Icon type="ios-navigate" :size="iconSize"></Icon> <span class="layout-text">TODOList</span> </Menu-item> </Menu> </i-col> <i-col :span="spanRight"> <div class="layout-header"> <i-button type="text" @click="toggleClick"> <Icon type="navicon" size="32"></Icon> </i-button> </div> <div class="layout-content"> <div class="layout-content-main">内容区域</div> </div> <div class="layout-copy"> 2011-2016 © demo </div> </i-col> </Row> </div> </template> <script> export default { data () { return { spanLeft: 5, spanRight: 19 } }, computed: { iconSize () { return this.spanLeft === 5 ? 14 : 24; } }, methods: { toggleClick () { if (this.spanLeft === 5) { this.spanLeft = 2; this.spanRight = 22; } else { this.spanLeft = 5; this.spanRight = 19; } } } } </script> <style scoped> .layout{ border: 1px solid #d7dde4; background: #f5f7f9; position: relative; border-radius: 4px; overflow: hidden; } .layout-breadcrumb{ padding: 10px 15px 0; } .layout-content{ min-height: 200px; margin: 15px; overflow: hidden; background: #fff; border-radius: 4px; } .layout-content-main{ padding: 10px; min-height:768px; } .layout-copy{ text-align: center; padding: 10px 0 20px; color: #9ea7b4; } .layout-menu-left{ background: #464c5b; } .layout-header{ height: 60px; background: #fff; box-shadow: 0 1px 1px rgba(0,0,0,.1); } .layout-logo-left{ width: 90%; height: 30px; background: #5b6270; border-radius: 3px; margin: 15px auto; text-align:center; line-height:30px; color:#fff; } .layout-ceiling-main a{ color: #9ba7b5; } .layout-hide-text .layout-text{ display: none; } .ivu-col{ transition: width .2s ease-in-out; } </style>
运行npm run dev:可看到如下效果:
接下来引入我们的vuex,使用npm install --save vuex ,并对main.js做如下修改:
import Vue from ‘vue‘ import IView from ‘iview‘; import Vuex from ‘vuex‘; import App from ‘./App.vue‘ import ‘iview/dist/styles/iview.css‘; import store from ‘./store‘; Vue.use(IView); Vue.use(Vuex); new Vue({ el: ‘#app‘, store, render: h => h(App) })
创建store.js,并添加如下代码(代码来源于上一篇博文中代码):
var list=[]; export default { state: { items: [], // todoContainer中items, //初始化表单所用 initItem: { title: ‘‘, desc: ‘‘, id: ‘‘ } }, mutations: { search (state, payload) { state.items = list.filter(v => v.title.indexOf(payload.title) !== -1); }, save (state, payload) { if (state.initItem.id) { var o = list.filter(v => v.id === payload.id); o.title = payload.title; o.desc = payload.desc; state.items = state.items.map(v => { if (v.id == payload.id) { return payload; } return v; }); } else { var id=state.items.length+1; state.items.push({id:id,title:payload.title, desc:payload.desc}); } list = state.items; }, remove (state, payload) { state.items = state.items.filter(v => v.id !== payload.id); }, edit (state, payload) { state.initItem = state.items.filter(v => v.id === payload.id)[0]; } } };
创建components文件夹,并按照单文件组件的规范创建组件:
SearchBar.vue
<template> <div class="row toolbar"> keyword: <Input type="text" v-model="keyword" ></Input> <Button type="primary" @click="search()">search</Button> </div> </template> <script> export default { data: function () { return { keyword: ‘‘ } }, methods: { search() { this.$store.commit("search", { title: this.keyword }); } } } </script>
TodoList.vue:
<template> <Table border :columns="columns" :data="items"></Table> </template> <script> export default{ data(){ return { columns:[ { title:‘Id‘, key:‘id‘ }, { title:‘title‘, key:‘title‘, }, { title:‘desc‘, key:‘desc‘ }, { title:‘actions‘, //TODO:操作 } ] } }, props:[ ‘items‘ ], methods:{ edit: function () { this.$store.commit(‘edit‘,this.todo); }, remove: function () { this.$store.commit(‘remove‘,{id:this.todo.id}); } } } </script>
TodoForm.vue:
<template> <div class="col-md-6"> <div> <label for="title">title:</label> <input type="hidden" v-bind:value="todo.id" /> <Input v-model="todo.title" ></Input> </div> <div> <label for="desc">desc</label> <Input v-model="todo.desc" ></Input> </div> <div> <Button type="primary" v-on:click="ok()">Ok</Button> </div> </div> </template> <script> export default{ props: [‘initItem‘], computed: { todo: function () { return { id: this.initItem.id, title: this.initItem.title, desc: this.initItem.desc }; } }, methods: { ok: function () { this.$store.commit("save",this.todo); } } } </script>
修改app.vue 完成组件注册和初始化:
<template> <div class="layout" :class="{‘layout-hide-text‘: spanLeft < 5}"> <Row type="flex"> <i-col :span="spanLeft" class="layout-menu-left"> <Menu active-name="1" theme="dark" width="auto"> <div class="layout-logo-left"> Demo Project </div> <Menu-item name="1"> <Icon type="ios-navigate" :size="iconSize"></Icon> <span class="layout-text">TODOList</span> </Menu-item> </Menu> </i-col> <i-col :span="spanRight"> <div class="layout-header"> <i-button type="text" @click="toggleClick"> <Icon type="navicon" size="32"></Icon> </i-button> </div> <div class="layout-content"> <div class="layout-content-main"> <search-bar></search-bar> <todo-list :items="items"></todo-list> <todo-form :init-item="initItem"></todo-form> </div> </div> <div class="layout-copy"> 2011-2016 © demo </div> </i-col> </Row> </div> </template> <script> import SearchBar from ‘./components/SearchBar.vue‘; import TodoForm from ‘./components/TodoForm.vue‘; import TodoList from ‘./components/TodoList.vue‘; export default { data () { return { spanLeft: 5, spanRight: 19 } }, components:{ ‘search-bar‘:SearchBar, ‘todo-form‘:TodoForm, ‘todo-list‘:TodoList }, computed: { iconSize () { return this.spanLeft === 5 ? 14 : 24; }, initItem: function () { return this.$store.state.initItem; }, items: function () { return this.$store.state.items; } }, methods: { toggleClick () { if (this.spanLeft === 5) { this.spanLeft = 2; this.spanRight = 22; } else { this.spanLeft = 5; this.spanRight = 19; } } } } </script> <style scoped> ..... </style>
此时保存,直接在浏览器可以看到如下效果:
今天时间不充足,重构就到这里,第一次使用单文件组件还是手生,代码调试比较费时间,一步一步的来吧。下一篇继续改造,里面包含了很多bug,大家可以试着修复或者完善一下。
good night。
原文:http://www.cnblogs.com/Johnzhang/p/7302492.html