zoukankan      html  css  js  c++  java
  • 一步一步学Vue(十二)

    为了提升代码的逼格,之后代码改为Vue文件组件,之前代码虽然读起来容易理解,而且适合在小的项目中使用,但是有如下缺点:

    • 全局定义(Global definitions) 强制要求每个 component 中的命名不得重复
    • 字符串模板(String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 
    • 不支持CSS(No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
    • 没有构建步骤(No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

    文件扩展名为 .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 &copy; 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{
             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 &copy; 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。

  • 相关阅读:
    Java SE(2)
    Java SE(1)
    第二次作业
    Markdown 进阶
    关于主题
    Markdown入门指南
    网络游戏架构与微服务架构简单对比
    微服务框架的存储架构
    轻量级微服务框架的通信架构
    页面静态化
  • 原文地址:https://www.cnblogs.com/Johnzhang/p/7302492.html
Copyright © 2011-2022 走看看