AngularJs1.x 和 Vue2.x

前言:这是很久之前(大概2017年)写的文档,Angular已经更新到4.x版本,此文章关于Angular部分已经不适用。

AngularJs概述

AngularJs是什么

AngularJS是一个开源的Web应用程序框架,诞生于2009年,现在由谷歌维护。

AngularJs的核心特性

  • MVVM
    是由MVC演变来的,是一种软件架构模式,是一种应用程序的设计思想(不是设计模式)。
    模型(Model):数据保存
    视图(View):用户界面
    控制器(Controller):业务逻辑
    视图模型(ViewModel):业务逻辑。区别:它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

MVVM 和 MVC 的区别:
MVVM中的View 和 ViewModel可以互相通信。也就是可以互相调用。
MVC中的是单向通信。也就是View跟Model,必须通过Controller来承上启下。

!不要深究到底是什么类型(MVC/MVVM),重要的是学会使用。
  • 模块化
    一个Module通常对应一个js文件,其中可以包括Controller(控制器)、Service(服务)、Filter(过滤器)、Directive(自定义指令)等。

  • 依赖注入
    调用者通声明某个组件就可以获得该组件的外部资源

  • 指令
    前缀为ng-的属性,作用是为DOM元素绑定数据、添加事件等(如ng-app、ng-model等)

  • 双向数据绑定
    模型和视图组件之间的数据自动同步,一般通过ng-model指令实现。

  • 控制器
    作用:

    1. 初始化视图中使用的数据
    2. 通过$scope对象把数据模型或函数行为暴露给视图
    3. 业务逻辑处理
  • 服务
    AngularJS配有多个内置服务,例如 $http可作为一个XMLHttpRequest请求。这些单一对象在应用程序只实例化一次。

  • 路由
    切换视图

Angular使用场景

  • AngularJS主要考虑的是构建CRUD应用,一般是:单页面的应用程序。

  • 单页面应用程序:
    单页 Web 应用 (single-page application 简称为 SPA) 是一种特殊的 Web 应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript 和 CSS。一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转。而是利用 JavaScript 动态的变换HTML的类(采用的是div切换显示和隐藏),从而实现UI与用户的交互。由于避免了页面的重新加载,SPA 可以提供较为流畅的用户体验。得益于ajax,我们可以实现无跳转刷新,又多亏了浏览器的histroy机制,我们用hash的变化从而可以实现推动界面变化。

Angular的基本使用

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body>
 
    <div ng-app="">
        <p>名字 : <input type="text" ng-model="name"></p>
        <h3>Hello {{ name }}</h3>
    </div>
 
</body>
</html>

实例解析:
ng-app:使用该指令自动启动一个AngularJS应用,指定AngularJS应用程序管理的边界,只有在ng-app内部的指令才会起作用
ng-model:绑定数据,在 input/select/textarea 标签中使用
{ { } }:使用 表达式 把数据绑定到 HTML。

指令

  • 将前缀为 ng- 的属性称之为指令,其作用是为DOM元素 绑定数据、添加事件 等
  • 指令包含:内置指令 和 自定义指令

常用指令

ng-app

  • 作用:该指令用来启动一个AngularJS应用
  • 理解:指定AngularJS应用程序管理的边界,只有在ng-app内部的指令才会起作用

ng-model

  • 作用:绑定数据,在 input/select/textarea 标签中使用

ng-click

  • 作用:定义元素被点击时的行为

实例:

    <body ng-app="">

        <p>点击按钮:</p>

        <button ng-click="count = count + 1" ng-init="count=0">{{count}}</button>

    </body>

ng-bind

<div ng-bind="vm.info"></div>
  • 作用:单向绑定,等同于 <div>{ { vm.info } }</div>

ng-repeat

<div ng-init="names=['Jani','Hege','Kai']">
  <ul>
    <li ng-repeat="x in names">
      { { x } }
    </li>
  </ul>
</div>

其它

  • ng-src src属性
  • ng-href href属性
  • ng-checked 选中状态
  • ng-selected 被选择状态
  • ng-disabled 禁用状态
  • ng-multiple 多选状态
  • ng-readonly 只读状态

表达式

<div ng-app="" ng-init="quantity=1;cost=5;firstName='Li';lastName='Bai'">   
        <p>{ { 1 + 8 } }</p>
        <p>{ { quantity * cost } }</p>
        <p>{ { firstName + " " + lastName } }</p>
    </div>
  • 作用:把数据绑定到 HTML,它们可以包含文字、运算符和变量。

module - 模块

模块是一个容器包含了应用程序的不同组成部分,并且这些内容必须要依附于一个模块
例如:controllers, services, filters, directives, configs 等

模块是应用程序的组成单元,例如:登录模块、注册模块、商品列表模块 等,这些模块
组合在一起构成了一个完整的应用程序。

  • 需要配合ng-app使用

创建模块

var app = angular.module(moduleName, []);
// 第一个参数:模块名称,字符串
// 第二个参数:数组,用来添加当前模块的依赖项

一个示例:

<body ng-app="myApp">
    <div ng-controller="myController" >
        <p>你好!{ { user.name } }</p>
    </div>
    <script>
        var app = angular.module("myApp", []);
        app.controller("myController", function($scope) {
            $scope.user = {name: 'Jack', age: 18};
        });
    </script>
</body>

// 也可以
angular.module("myApp",[])
.controller("myController",function($scope){ ... });

controller - 控制器

  • 需要配合ng-controller指令来使用

创建控制器

app.controller(ctrlName,function(){ ... });

安全方式创建

app.controller("ctrlName", ["$scope", "$log", function($scope, $log) {
    $log.log("打印日志了");
}]);

问题:项目上线的时候,会进行代码压缩,$scope会被修改
解决:代码会被压缩和混淆,但是 字符串 是不会被压缩的
// 第一个参数:控制器的名称
// 第二个参数:数组,最后一项表示回调函数,除此之外其他的参数表示依赖的参数列表

控制器的作用

  • 初始化数据,存储数据的容器
  • 通过$scope对象把数据模型或函数行为暴露给视图
  • 监视模型的变化,做出相应的逻辑处理

$scope的说明

  • $scope是控制器和视图之间的桥梁,用于在控制器和视图之间传递数据
  • 在控制器中暴露 数据模型(数据和行为),在视图中通过指令或表达式来使用
  • 所有的 scope 都继承自rootScope

$rootScope

AngularJS 应用启动并生成视图时,会将根 ng-app 元素与 rootScope 进行绑定.rootScope 是所有 $scope 的最上层对象,可以理解为一个 AngularJS 应用中的全局作用域对象,所以为它附加太多逻辑或者变量并不是一个好主意,和污染 Javascript 全局作用域是一样的。

$watch 监听数据

app.controller("myCtrl", function($scope) {
    $scope.name = "jack";
    // 参数一:表示监听的$scope中的属性名称,类型为:字符串
    // 参数二:表示数据变化执行的回调函数,有两个参数分别是:当前值与变化之前的值
    // 参数三:比较方式,false:表示比较引用;true:表示比较值。默认值为false
    $scope.$watch("name", function(newValue, oldValue) {
        // 只要被监听的数据发生变化,就会指定该回调函数中的代码!
        // 略过第一次执行
        if(newValue === oldValue) return;
    });
});
  • 作用:监听$scope中数据模型的变化,无法监视其他的数据(例如,普通变量)

控制器的基本使用

<div ng-controller="myCtrl" ng-click="click()">{ { a } }</p>

<script>
app.controller("myCtrl",function($scope){
    $scope.a = '123';

    $scope.click = function(){
      $scope.a = '456';
    }
})
</script>

AngularJS的一般开发流程

1 引入 angular.js 文件
2 创建模块:angular.module('模块名', [])
3 在页面中指定ng-app="模块名",告诉NG管理指定的页面部分
4 创建控制器:模块名.controller('控制器名', function() {})
5 在页面中指定ng-controller="控制器名",指定管理内容的控制器
6 建模(根据页面原型抽象出数据模型),最终得到视图模型(ViewModel)
7 将抽象好的数据,添加到 $scope中,即:暴露数据和行为给视图
8 在页面中使用 ng-model 或者 { { } } 拿到并绑定数据

模块的划分

1 按照模块划分(推荐)
2 按照文件类型划分

按照模块划分

根据项目中具体的功能模块进行划分
比如:登录模块、注册模块、编写博客 等等不同的功能模块
每个功能模块都有自己的 Model View Controller 对应的文件
开发过程中,每个人完成一个独立的模块

按照文件划分

根据文件的功能进行划分
将所有的文件放到3个文件夹中:M、V、C

事件

AngularJs有自己的HTML事件指令。我们可以把相关事件的处理函数直接写在 DOM 中,这样做的最大好处就是可以从 DOM 结构上看出业务处理的形式,你知道当你点击这个节点时哪个函数被执行了。

  • ng-click 点击事件
  • ng-dblclick 双击事件
  • ng-blur 元素失去焦点事件
  • ng-change 元素的值发生改变事件
  • ng-mousedown 按下鼠标按键
  • ng-mouseenter 鼠标指针穿过被选元素
  • ng-mouseleave 鼠标指针离开元素
  • ng-mousemove 鼠标指针在指定的元素中移动
  • ng-mouseover 鼠标指针位于元素上方
  • ng-mouseup 在元素上放松鼠标按钮
  • ng-submit 提交表单事件

angular 作用域

Angular 控制器中的 $scope 在各作用域之间遵循 JS 的对象原型继承方式。当子作用域中没有该对象时,默认向上(父作用域)寻找;子作用域有该对象时,使用子作用域对象。
父作用域包含直接父级和祖先,子作用域包含直接子级和更下层级

父子作用域

示例一:

<div ng-controller="ParentCtrl">
    { { title } }
    <div ng-controller="ChildCtrl">{ { title } }</div>
    }
</div>

app.controller("ParentCtrl", [ '$scope', function($scope){
    $scope.title = "Parent";
 }]);
app.controller("ChildCtrl", [ '$scope', function($scope){
    
 }]);

父子controller里都使用了title,但是子controller里没有定义title
效果如下:
Parent
Parent

示例二:

app.controller("ParentCtrl", [ '$scope', function($scope){
    $scope.title = "Parent";
 }]);
app.controller("ChildCtrl", [ '$scope', function($scope){
    $scope.title = "Child";    
}]);

此时父子controller中都有 $scope.title ,它们互不影响。
效果如下:
Parent
Child

过滤器

filter

过滤输入
    <div ng-app="filterApp" ng-controller="filterCtrl">
        <p><input type="text" ng-model="search"></p>
        <ul>
          <li ng-repeat="x in names | filter:search">
            {{ x }}
          </li>
        </ul>
    </div>

    <script>
        angular.module("filterApp",[])
        .controller("filterCtrl",function($scope){
            $scope.names = ['LiBai','XiaoMing','Hong','Anny','Jack'];
        })
    </script>

自定义过滤器

过滤器的基本定义方式

    var app = angular.module('app', []);
    app.filter('map', function(){
      var filter = function(input){
        return input + '...';
      };
      return filter;
    });

服务(Service)

什么是服务

  • 在 AngularJS 中,服务是一个函数或对象,可在你的 AngularJS 应用中使用。
  • 一般主要封装针对于Model的CRUD
  • AngularJS 内建了30 多个服务。log、http 等以$开头的服务都是Angular的内置服务。

内置服务

$location,可以返回当前页面的 URL 地址。

    <div ng-app="myApp" ng-controller="myCtrl">
    <p>{ { myUrl } }</p>
    </div>

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $location) {
        $scope.myUrl = $location.absUrl();
    });
  • 注意 $location 服务是作为一个参数传递到 controller 中。如果要使用它,需要在 controller 中定义。

$http,向服务器请求数据

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $http) {
        $http.get("welcome.htm").then(function (response) {
            $scope.myWelcome = response.data;
        });
    });
通用方法格式:
    $http({
        method: 'GET',
        url: '/someUrl'
    }).then(function successCallback(response) {
            // 请求成功执行代码
        }, function errorCallback(response) {
            // 请求失败执行代码
    });
POST 与 GET 简写方法格式:
    $http.get('/someUrl', config).then(successCallback, errorCallback);
    $http.post('/someUrl', data, config).then(successCallback, errorCallback);

$timeout,对应了 JS window.setTimeout 函数。

    <p>{{ msg }}</p>

    var app = angular.module("app",[]);
    app.controller("myCtrl",function($scope,$timeout){
        $scope.msg = "Hello Angular";
        $timeout(function(){
            $scope.msg = "你学会了吗?";
        },2000)
    })

$interval,对应了 JS window.setInterval 函数。

    <div ng-app="myApp" ng-controller="myCtrl"> 
        <p>现在时间是:</p>
        <h3>{{ theTime }}</h3>
    </div>

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $interval) {
        $scope.theTime = new Date().toLocaleTimeString();
        $interval(function () {
            $scope.theTime = new Date().toLocaleTimeString();
        }, 1000);
    });

依赖注入

什么是依赖注入

依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分。

该模式分离了客户端依赖本身行为的创建,这使得程序设计变得松耦合,并遵循了依赖反转和单一职责原则。与服务定位器模式形成直接对比的是,它允许客户端了解客户端如何使用该系统找到依赖

例如:

app.controller('myCtrl',function($scope,$element){     
    ...
})

注意那两个参数: $scope$element ,这是两个很有意思的东西。总的来说,它们是参数,这没什么可说的。但又不仅仅是参数:你换个名字代码就不能正常运行了。

事实上,这两个参数,除了完成“参数”的本身任务之外,还作为一种语法糖完成了“依赖声明”的任务。本来这个函数定义,完整的写法应该写成:

app.controller('myCtrl',['$scope','$element',function($scope,$element){
    ...
}])

这样就很明显,表示一个函数,它依赖于两个东西,然后这两个东西会依次作为参数传入。在实际开发中,建议这种写法。

路由

AngularJS 路由允许我们通过不同的 URL 访问不同的内容。
通过 AngularJS 可以实现多视图的单页Web应用(single page web application,SPA)。

理解

通常我们的URL形式为 http://xxx.com/first/page,但在单页Web应用中 AngularJS 通过 # + 标记 实现,例如:
http://xxx.com/#/first
http://xxx.com/#/second
http://xxx.com/#/third

一个示例

    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>

    // 1. 载入实现路由的js文件 angular-route.js
    <script src="http://apps.bdimg.com/libs/angular-route/1.3.13/angular-route.js"></script>

    <body ng-app="myApp">
        <ul>
            <li><a href="#/">首页</a></li>
            <li><a href="#/computers">电脑</a></li>
            <li><a href="#/printers">打印机</a></li>
            <li><a href="#/blabla">其他</a></li>
        </ul>

        // 3. 使用 ng-view 指令展示路由对应的内容
        <div ng-view></div>

        <script>
            // 2. 包含了 ngRoute 模块作为主应用模块的依赖模块 
            angular.module("myApp",['ngRoute'])

            // 4. 配置 $routeProvider 定义路由规则
            .config(['$routeProvider',function($routeProvider){
                $routeProvider
                .when('/',{template: '这是首页' })
                .when('/computers',{template: '这是电脑分类页面'})
                .when('/printers',{template: '这是打印机页面'})
                .otherwise({redirectTo: '/'});
            }]);
        </script>
    </body>
  • Angular模块的 config 函数用于配置路由规则。
  • $routeProvider 为我们提供了 when(path,object) & otherwise(object) 函数按顺序进行路由配置
  • routeParams 可以获取当前路径参数 例如: // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby // 路由规则: /Chapter/:chapterId/Section/:sectionId // 结果:routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}

自定义指令

除了 AngularJS 内置的指令外,我们还可以创建自定义指令。
你可以使用 .directive 函数来添加自定义的指令。
要调用自定义指令,HTML 元素上需要添加自定义指令名。
使用驼峰法来命名一个指令, myDirective, 但在使用它时需要以 - 分割, my-directive:

这是 ng 最强大的一部分,也是最复杂最让人头疼的部分。

目前我们看到的所谓“模板”系统,只不过是官方实现的几个指令而已。这意味着,通过自定义各种指令,我们不但可以完全定义一套“模板”系统,更可以把 HTML 页面直接打造成为一种 DSL (领域特定语言)。

一个示例

// 定义自定义的HTML标签
<my-student></my-student>

// 定义自定义指令来处理自定义HTML标签
var app = angular.module('myApp',[]);
app.directive('myStudent',function(){
    var directive = {};
    directive.template = '<ul><li ng-repeat="student in students">{ { student.name } }, { { student.age } }</li></ul>';
    return directive;
})
app.controller('myCtrl',function($scope){
    $scope.students = [
        {name : 'Jack', age: 19},
        {name : 'Bai', age: 17},
        {name : 'Ming', age: 19},
        {name : 'Hong', age: 18},
        {name : 'Anny', age: 19}
    ];
})

指令的说明

  • 作用:实现语义化标签

我们常用的HTML标签是这样的:

<div>
    <span>一点点内容</span>
</div>

而使用AngularJS的directive(指令)机制,我们可以实现这样的东西:

<tabpanel>
    <panel>子面板1</panel>
    <panel>子面板2</panel>
</tabpanel>

示例一

<hello></hello>

对于以上代码里面的<hello>标签,浏览器显然是不认识的,它唯一能做的事情就是无视这个标签。那么,为了让浏览器能够认识这个标签,我们需要使用Angular来定义一个hello指令(本质上说就是自己来把<hello>这种玩意儿替换成浏览器能识别的那些标准HTML标签)。

var appModule = angular.module('app', []);
appModule.directive('hello', function() {
    return {
        restrict: 'E',
        template: '<div>Hi there</div>',
        replace: true
    };
});

实际产生的标签结构是这样的:

<div>Hi there</div>
  • 理解:
    可以看到,<hello>已经被<div>Hi there</div>这个标签替换掉了,这也是以上JS代码里面 replace:true 这行配置的作用,代码里面的 template 配置项就是我们要的div标签,如果我们需要替换的HTML标签很长,这时候我们可以用templateUrl来替代template,从而可以把模板写到一个独立的HTML文件中。至于 restrict:'E'这个配置项的含义,请看:

  • restrict 配置项:
    E ===> 标签 ===> <my-dir></my-dir>
    A ===> 属性 ===> <div my-dir></div>
    C ===> 样式 ===> <div class="my-dir"></div>
    M ===> 注释 ===> ``

指令的使用

指令可以作为标签使用,也可以作为属性使用。在使用时,它的引用名可以是:

  • ng:bind
  • ng_bind
  • ng-bind
  • x-ng-bind
  • data-ng-bind

Vue

Vue.js概述

Vue.js是什么

Vue是一个轻巧、高性能、可组件化的MVVM框架,是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。

Vue.js的核心特性

  1. 轻量级的框架
  2. 双向数据绑定
  3. 指令
  4. 组件化

Vue.js与其它框架的区别

  1. Vue.js与AngularJs的区别
    相同点:
    (1)都支持指令:内置指令和自定义指令。
    (2)都支持过滤器:内置过滤器和自定义过滤器。
    (3)都支持双向数据绑定。
    (4)都不支持低端浏览器。

不同点:
(1)AngularJs的学习成本更高
(2)在性能上,AngularJS依赖对数据做脏检查,所以watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。

  1. Vue.js与React的区别
    相同点:
    (1)中心思想相同:一切都是组件,组件实例之间可以嵌套。
    (2)都是以插件的形式加载Ajax、Route等功能。
    (3)在组件开发中都支持mixins的特性。

不同点:
React依赖Virtual DOM(虚拟DOM),而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出的结果做脏检查。

Vue.js的应用场景:

针对具有复杂交互逻辑的前端应用。
当前端和数据做一些操作的时候,可以通过AJAX请求对后端做数据持久化,不需要刷新整个页面,只需要改动DOM里需要改动的那部分数据。特别是移动端应用场景,刷新页面太昂贵,会重新加载很多资源,虽然有些会被缓存,但是页面的DOM,JS,CSS都会被页面重新解析一遍,因此移动端页面通常会做出SPA单页应用。

Vue.js的基本使用

声明式渲染

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
    </head>
    <body>
     
        <div id="app">
          {{ message }}
        </div>

        <script>
            var app = new Vue({
              el: '#app',
              data: {
                message: 'Hello Vue!'
              }
            })
        </script>
     
    </body>
</html>

我们已经生成了第一个 Vue 应用程序!这看起来和渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被关联在一起,所有的数据和 DOM 都是响应式的。我们如何对这一切清晰领会?只需打开你的浏览器的 JavaScript 控制台(现在,就在当前页面打开),然后设置 app.message 的值,你将看到上面的示例所渲染的 DOM 元素会相应地更新。

Vue实例

创建一个Vue实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的。
语法格式如下:

var vm = new Vue({
  // 选项
})

接下来让我们通过实例来看下 Vue 构造器中需要哪些内容:

<div id="vue_app">
    <h5>firstName: {{firstName}}</h5>
    <h5>lastName: {{lastName}}</h5>
    <h5>{{fullName()}}</h5>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: '#vue_app',
        data: {
            firstName: "李",
            lastName: "白",
            alexa: "10000"
        },
        methods: {
            fullName: function() {
                return  this.firstName + this.lastName;
            }
        }
    })
</script>

可以看到在 Vue 构造器中有一个el 参数,它是 DOM 元素中的 id。在上面实例中 id 为 vue_app,在 div 元素中:

<div id="vue_app">...</div>

这意味着 Vue 实例只作用在指定的DOM元素内,元素外不受影响。

定义数据对象

data 是 Vue 实例的数据对象。当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些数据改变时,视图会进行重渲染,即数据驱动模型。

例:

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})

// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

只有当实例被创建时 data 中存在的属性才是响应式的。如果在晚些时候需要一个属性,但是一开始它为空或不存在,那么需要设置一些初始值。

例:

data: {
    title: '',
    messages: '',
    num: 0
      },

除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。例如:

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

定义方法

methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。

例:

var vm = new Vue({
  data: { a: 1 },
  methods: {
    plus: function () {
      this.a++
    }
  }
})
vm.plus();
vm.a // 2

{{ }}

{{ }}用于输出对象属性和函数返回值。它会将数据解释为普通文本,而非 HTML 代码。

例:

<h5>firstName: {{firstName}}</h5>
<h5>lastName: {{lastName}}</h5>
<h5>{{fullName()}}</h5>

模板语法

插值

文本

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<span>Message: {{ msg }}</span>

Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新。

通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

<span v-once>这个将不会改变: {{ msg }}</span>

HTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

<div id="app">
    <div v-html="message"></div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: '<h1 style="color: red;">红色的大标题</h1>'
  }
})
</script>
表达式

在模板中,还可以绑定表达式:

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。

指令

指令是带有 v- 前缀的特殊属性。
指令用于在表达式的值改变时,将某些行为应用到 DOM 上。如下例子:

<div id="app">
    <p v-if="seen">现在你看到我了</p>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    seen: true
  }
})
</script>
参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML 属性:

<div id="app">
    <pre><a v-bind:href="url">vue.js</a></pre>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    url: 'http://www.cnblogs.com'
  }
})
</script>

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">

在这里参数是监听的事件名。

缩写

对于一些频繁用到的指令,例如 v-bind 和 v-on 这两个最常用的指令,Vue.js 为其提供了特定简写:

  • v-bind 简写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>
  • v-on 简写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>
修饰符

修饰符是以半角句号 . 指明的特殊后缀,用于指出一个指定应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

<form v-on:submit.prevent="onSubmit"></form>
用户输入

在 input 输入框中我们可以使用 v-model 指令来实现双向数据绑定:

<div id="app">
    <p>{{ message }}</p>
    <input v-model="message">
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  }
})
</script>

v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值。因为它会选择 Vue 实例数据来作为具体的值。应该通过 JavaScript 在组件的 data 选项中声明初始值。

按钮的事件我们可以使用 v-on 监听事件,并对用户的输入进行响应。
以下实例在用户点击按钮后对字符串进行反转操作:

<div id="app">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转字符串</button>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'Hello World!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    }
  }
})
</script>

过滤器

Vue.js 允许自定义过滤器,被用作一些常见的文本格式化。由"管道符"指示, 格式如下:

<!-- 在两个大括号中 -->
{{ message | capitalize }}

<!-- 在 v-bind 指令中 -->
<div v-bind:id="rawId | formatId"></div>

过滤器函数接受表达式的值作为第一个参数。
以下实例对输入的字符串第一个字母转为大写:

<div id="app">
  {{ message | capitalize }}
  <input v-model="message">
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    message: 'hello'
  },
  filters: {
    capitalize: function (value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
})
</script>

过滤器可以串联:

{{ message | filterA | filterB }}

过滤器是 JavaScript 函数,因此可以接受参数:

{{ message | filterA('arg1', arg2) }}

条件与循环

v-if v-else-if v-else

条件判断使用 v-if 指令:

<div id="app">
    <p v-if="seen">哈哈你看到我了</p>
    <template v-if="ok">
      <h1>吃鱼大神</h1>
      <p>为了梦想!</p>
      <p>巴拉巴拉小魔仙!</p>
    </template>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    seen: true,
    ok: false
  }
})
</script>

这里, v-if 指令将根据表达式 seen 的值(true 或 false )来决定是否插入 p 元素。

再看一个例子:

<div id="app">
    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>
</div>
    
<script>
new Vue({
  el: '#app',
  data: {
    type: 'C'
  }
})
</script>

v-else 、v-else-if 必须跟在 v-if 或者 v-else-if之后。

v-show

我们也可以使用 v-show 指令来根据条件展示元素:

<div id="app">
    <h1 v-show="ok">Hello!</h1>
</div>
  
<script>
new Vue({
  el: '#app',
  data: {
    ok: true
  }
})
</script>
  • v-if 与 v-show 的区别
  1. v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  2. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
  3. 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
  4. 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用v-if 较好。
v-for

循环使用 v-for 指令。
v-for 可以绑定数据到数组来渲染一个列表:

<div id="app">
  <ol>
    <li v-for="item in items">
      {{ item.name }}
    </li>
  </ol>
</div>
 
<script>
new Vue({
  el: '#app',
  data: {
    items: [
      { name: 'Runoob' },
      { name: 'Google' },
      { name: 'Taobao' }
    ]
  }
})
</script>

事件处理

事件监听可以使用 v-on 指令:

<div id="app">
  <button v-on:click="counter += 1">增加 1</button>
  <p>这个按钮被点击了 {{ counter }} 次。</p>
</div>
 
<script>
new Vue({
  el: '#app',
  data: {
    counter: 0
  }
})
</script>
事件处理方法

许多事件处理逻辑会比上面的实例更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

例:

<div id="app">
   <!-- `greet` 是在下面定义的方法名 -->
  <button v-on:click="greet">Greet</button>
</div>
 
<script>
var app = new Vue({
  el: '#app',
  data: {
    name: 'Vue.js'
  },
  // 在 `methods` 对象中定义方法
  methods: {
    greet: function (event) {
      // `this` 在方法里指当前 Vue 实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM 事件
      if (event) {
          alert(event.target.tagName)
      }
    }
  }
})
// 也可以用 JavaScript 直接调用方法
app.greet() // -> 'Hello Vue.js!'
</script>

Vue.js 组件

组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:

注册一个全局组件语法格式如下:

Vue.component(tagName, options)

tagName 为组件名,options 为配置选项。注册后,我们可以使用以下方式来调用组件:

<tagName></tagName>
全局组件

所有实例都能用全局组件。
注册一个简单的全局组件 temp,并使用它:

<div id="app">
    <temp></temp>
</div>
 
<script>
// 注册
Vue.component('temp', {
  template: '<h1>自定义组件!</h1>'
})
// 创建根实例
new Vue({
  el: '#app'
})
</script>

要在父实例中使用这个组件,必须要在实例创建前注册,之后就可以用<my-component></my- component>的形式来使用组件了

template的DOM结构必须被一个元素包含, 如果直接写成“这里是组件的内容”, 不带<div></ div >是无法渲染的。(而且最外层只能有一个根的<div>标签)

组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
例:

<div id="components-demo">
  <button-counter></button-counter>
</div>

<script>
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

// 创建根实例
new Vue({ el: '#components-demo' })
</script>

可以将组件进行任意次数的复用:

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>
局部组件

我们也可以在实例选项中注册局部组件,这样组件只能在这个实例中使用:

<div id="app">
    <temp></temp>
</div>
 
<script>
var Child = {
  template: '<h1>自定义组件!</h1>'
}
 
// 创建根实例
new Vue({
  el: '#app',
  components: {
    'temp': Child     // <temp> 将只在父模板可用
  }
})
</script>
Props

组件接受的选项大部分与Vue实例一样,而选项props是组件中非常重要的一个选项。在 Vue 中,父子组件的关系可以总结为 props down, events up。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。

Prop 的大小写:
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

一、基本用法
组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。

在组件中,使用选项 props 来声明需要从父级接收的数据, props 的值可以是两种, 一种是字符串数组,一种是对象。

  • 字符串数组
<div id="app">
    <my-component message="数据来自父组件"></my-component>
</div>

<script>
  Vue.component('my-component',{
      props: ['message'],
      template: '<div>{{message}}</div>'
  });

  var app = new Vue({
      el: '#app'
  }); 
</script>    

渲染后的结果为:

<div id="app">
  <div>数据来自父组件</div>
</div>

props 中声明的数据与组件data 函数return 的数据主要区别就是props 的来自父级,而data 中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板template 及计算属性computed和方法methods 中使用。

上例的数据message 就是通过props 从父级传递过来的,在组件的自定义标签上直接写该props 的名称,如果要传递多个数据,在props 数组中添加项即可。

有时候,传递的数据并不是直接写死的,而是来自父级的动态数据,这时可以使用指令v -bind来动态绑定props 的值,当父组件的数据变化时,也会传递给子组件。

<div id="app">
  <input type="text" v-model="text">
  <my-component :my-text="text"></my-component>
</div>

<script>
  Vue.component('my-component',{
      props: ['myText'],
      template: '<div>{{myText}}</div>'
  });

  var app5 = new Vue({
      el: '#app',
      data: {
          text: '动态传递父组件数据'
      }
  });
</script>

注意:

  1. 如果你要直接传递数字、布尔值、数组、对象,而且不使用v-bind ,传递的仅仅是字符串。
<!-- 传递了一个字符串 "1" -->
<temp some-prop="1"></temp>

<!-- 传递真正的数值 1  -->
<temp v-bind:some-prop="1"></temp>
  1. 如果你想把一个对象的所有属性作为 prop 进行传递,可以使用不带任何参数的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一个 todo 对象:
todo: {
  text: 'Learn Vue',
  isComplete: false
}

然后:

<todo-item v-bind="todo"></todo-item>

将等价于:

<todo-item v-bind:text="todo.text" v-bind:is-complete="todo.isComplete"></todo-item>
自定义事件

当子组件需要向父组件传递数据时,就要用到自定义事件。
子组件用emit ()来触发事件,父组件用on()来监昕子组件的事件。
父组件可以直接在子组件的自定义标签上使用v-on 来监昕子组件触发的自定义事件,如

<div id="app">
  <p>总数:{{total}}</p>
  <my-component @add="getTotal" @cut="getTotal"></my-component>
</div>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 一:什么是闭包?闭包的用处? (1)闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就 是将函数内部和函数外...
    xuguibin阅读 9,436评论 1 52
  • AngularJS是什么? AngularJs(简称ng)是一个用于设计动态web应用的结构框架。首先,它是一个框...
    强哥科技兴阅读 1,250评论 0 1
  • AngularJS是什么?AngularJs(后面就简称ng了)是一个用于设计动态web应用的结构框架。首先,它是...
    200813阅读 1,568评论 0 3
  • Vue是一个前端js框架,由尤雨溪开发,是个人项目 Vue近几年来特别的受关注,三年前的时候angularJS霸占...
    6e5e50574d74阅读 547评论 0 0
  • 从感性的角度讲,我是不屑于用VUE,觉得react套件用起来更顺手,但是vue现在越来火,所以也不得入vue(杂烩...
    zhoulujun阅读 1,422评论 0 1