• [问题求助] 【OBS产品】【安装】Angular项目集成 OBS的对于的sdk,编译报错,无法使用
    安装 obs,使用的 npm install esdk-obs-nodejs 进行安装安装之后,项目编译就报错了(Angular12项目)在使用下面语句引入时,就会报错了var ObsClient = require('esdk-obs-nodejs');有没有遇到同样问题的,烦请大佬帮忙解答一下啊,感谢~
  • [活动打卡] 【移动应用开发全栈成长计划】第三阶段·小程序和Angular框架篇 · 每章随堂测验打卡帖
    亲爱的开发者们大家心心念念的【移动应用开发全栈成长计划】第三阶段小程序&Angular框架篇内容已开课我们也特别为大家设置了【学习任务打卡】环节为的就是让大家巩固学习成果,紧跟学习进度请将需要完成的每章随堂测验按要求回复到本帖下方按要求格式回复即可获得积分累计阶段奖品,还能有机会获得附加幸运奖哦~征集时间2021.03.22-2021.05.09 23:59征集要求在本帖中,回复自己本章随堂测验内容打卡要求在本帖中,回复对应章节的随堂测验并提交截图 ↓章节作业位置:打卡回复格式:华为云ID+作业完成截图,如图所示↓↓↓华为云ID:grandmaster奖励方式每周每篇有效笔记可获得2积分阶段学习将以积分排行榜的形式,对前300名小伙伴进行奖励哦!此外还有附加奖励,在完成每章随堂测验打卡的用户中,抽取10位幸运奖由于第三阶段课程内容较多,每章随堂测验抽奖与每周读书笔记抽奖改为每两周抽取一次奖励华为云定制敏捷扑克牌 各阶段积分排行榜奖品三个阶段总积分排行榜奖品 活动注意事项1. 学习任务提交后,小助手会在本阶段学习周期内,按序完成审核,并增加活动积分;2. 本次活动通过完成提交学习笔记任务,可获得的积分上限为2积分/每章;3. 请务必按照上述要求提交内容,以免影响积分增加;4. 若积分值相同则以完成学习任务的时间先后排序,其中任务完成时间的判定优先级为:阶段考核>问答官>分享转发>每周学习笔记>每章随堂测验;5. 其他积分获取方式请查看活动社群公告。想了解更多关于全栈成长计划课程内容请移步主帖:https://bbs.huaweicloud.com/forum/thread-84766-1-1.html除了本帖任务,其他学习任务可以通过以下链接进行查看:查看每周读书笔记打卡帖:https://bbs.huaweicloud.com/forum/thread-114924-1-1.html查看问答官排位打卡帖:https://bbs.huaweicloud.com/forum/thread-114947-1-1.html
  • [技术干货] Angular 单元测试
    隔离程序的每个部件,在隔离环境中运行测试用例。karma.conf.js :文件是为了告知 Karma 需要启用哪些插件、加载哪些测试脚本、需要哪些测试浏览器环境、测试报告通知方式、日志等等。具体配置内容参见 karma.conf.js。https://karma-runner.github.io/5.0/config/configuration-file.html>测试文件的扩展名必须是 .spec.ts,这样工具才能识别出它是一个测试文件,也叫规约(spec)文件"test": {     "options": {         "codeCoverage": true     } }Jasmine Angular 单元测试是使用 Jasmine 框架来编写的。基础知识describe(string, function):是 Jasmine 的全局函数,可以理解为一个测试集(Test Suite),主要功能是用来划分单元测试的。describe 可以嵌套使用。it(string, function):可以理解为测试用例。Specs 通过调用 it 的全局函数来定义。每个 Spec 包含一个或多个 expectations 期望值来测试需要测试代码。Jasmine 中的每个 expectation 是一个断言,可以是 true 或者 false。当每个 Spec 中的所有 expectations 都是 true,则通过测试。有任何一个 expectation 是 false,则未通过测试。而方法的内容就是测试主体。每个 Matchers 实现一个布尔值,在实际值和期望值之间比较。它负责通知 Jasmine,此 expectation 是真或者假。然后 Jasmine 会认为相应的 spec 是通过还是失败。所有的 expect 都可以使用 not 表示否定的断言。JavaScript 的作用域的规则适用,所以在 describe 定义的变量对 Suite 中的任何 it 代码块都是可见的。beforeAll:每个 suite(即 describe)中所有 spec(即 it)运行之前运行,整个suite里只运行一次afterAll:每个 suite(即 describe)中所有 spec(即 it)运行之后运行 xdescribe:该 describe下的所有 it 将被忽略,jasmine 将直接忽略这些it,因此不会被运行 // 引入相关模块 import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { BackupListComponent } from './backup-list.component'; import { DebugElement } from '@angular/core'; describe('HorizontalGridComponent test', () => {    let component: HorizontalGridComponent;  let fixture: ComponentFixture<HorizontalGridComponent>;     // 配置 TestBed 环境    beforeEach(async(() => {       TestBed.configureTestingModule({          imports: [            HttpClientTestingModule          ],          declarations: [HorizontalGridComponent],   }).compileComponents();    }));    beforeEach(() => {         // 创建一个HorizontalGridComponent 的实例   fixture = TestBed.createComponent(HorizontalGridComponent);         // 使用 fixture.componentInstance 来访问组件实例   component = fixture.componentInstance;         // TestBed.createComponent 不能触发变更检测,使用 detectChanges() 触发检查。   fixture.detectChanges();  });     it('should create', () => {   expect(fixture).toBeDefined();        expect(component).toBeDefined();  });  it('should have <h3> with "Hello"', () => {         // const El: HTMLElement = fixture.nativeElement;         const De: DebugElement = fixture.debugElement;         const El: HTMLElement = De.nativeElement; // nativeElement相当于debugElement的一个属性         // js原生的querySelector api去获取h3标签         const p = El.querySelector('h3');         // 判断h3标签的内容         expect(p.textContent).toEqual('Hello');     });     it('should find the <h3> with fixture.debugElement.query(By.css)', () => {         const De: DebugElement = fixture.debugElement;         const El = De.query(By.css('h3')); // nativeElement相当于debugElement的一个属性         // js原生的querySelector api去获取h3标签         const p: HTMLElement = El.nativeElement;         // 判断h3标签的内容         expect(p.textContent).toEqual('Hello');     });    afterEach(() => {       TestBed.resetTestingModule();    }); });置好 TestBed 之后,调用它的 createComponent() 方法,它会创建一个 HorizontalGridComponent 的实例,把相应的元素添加到测试运行器的 DOM 中,然后返回一个 ComponentFixture 对象。ComponentFixture 用来与所创建的组件及其 DOM 元素进行交互。获取组件元素 nativeElement 和 DebugElement。 前者原生 DOM 元素,属性取决于运行环境,如果是浏览器,就提供浏览器的一些api,后者是由 Angular 进行包装可以安全的横跨其支持的所有平台运行并提供诸如 query 或者 triggerEventHandler 事件dom操作等方法。TestBed.createComponent 不能触发变更检测,使用 detectChanges() 触发检查。运行完成后,控制台输出:chrome浏览器输出:
  • [技术干货] 三大前端技术(React,Vue,Angular)探密
    【引言】前段时间写过一篇关于前端技术的概括性文章《前端技术的选择]》(http://3ms.huawei.com/km/blogs/details/7971337),本文就对于当下顶级的前端开发技术做个相对详尽的探究,目的是为了解开这些前端技术的面纱,看看各自的庐山真面目。 【React】React(也被称为React.js或ReactJS)是一个用于构建用户界面的JavaScript库。它由Facebook和一个由个人开发者和公司组成的社区来维护。 React可以作为开发单页或移动应用的基础。然而,React只关注向DOM渲染数据,因此创建React应用通常需要使用额外的库来进行状态管理和路由,Redux和React Router分别是这类库的例子。基本用法下面是一个简单的React在HTML中使用JSX和JavaScript的例子。 <div id="myReactApp"></div> <script type="text/babel">  function Greeter(props) {    return <h1>{props.greeting}</h1>;  }  var App = <Greeter greeting="Hello World!" />;  ReactDOM.render(App, document.getElementById("myReactApp"));</script>  Greeter函数是一个React组件,它接受一个属性问候语。变量App是Greeter组件的一个实例,其中问候语属性被设置为 "Hello World!"。然后,ReactDOM.render方法将我们的Greeter组件渲染在DOM元素(id为 myReactApp)中。 在web浏览器中显示时,结果将是: <div id="myReactApp">  <h1>Hello World!</h1></div>  显著特点 组件化React代码由称为组件的实体组成。组件可以使用React DOM库渲染到DOM中的一个特定元素。当渲染一个组件时,可以传入被称为 "props "的值。 ReactDOM.render(<Greeter greeting="Hello World!" />, document.getElementById('myReactApp'));  React中声明组件的两种主要方式是通过功能函数组件和基于类的组件。 功能函数组件 功能组件是用一个函数声明,用来返回一些JSX。 const Greeting = (props) => <div>Hello, {props.name}!</div>;  类组件基于类的组件是使用ES6类来声明的。它们也被称为 "有状态 "组件,因为它们的状态可以在整个组件中保持,并且可以通过props传递给子组件。class ParentComponent extends React.Component {  state = { color: 'green' };  render() {    return (      <ChildComponent color={this.state.color} />    );  }} 虚拟 DOM另一个值得注意的特点是React使用了虚拟文档对象模型,也就是虚拟DOM。React创建了一个内存中的数据结构缓存,计算得出变化差异,只渲染实际变化的子组件, 从而高效地更新浏览器显示的DOM。 生命周期方法生命周期方法是指在组件的生命周期内,允许在设定的点执行代码的hooks处理函数。 l  shouldComponentUpdate允许开发者在不需要渲染的情况下,通过返回false来防止不必要的重新渲染组件。l  componentDidMount是在组件 "挂载 "后调用的(组件已经在用户界面中创建了,通常是通过将其与DOM节点关联起来)。这通常用于通过API从远程数据源触发数据加载。l  componentWillUnmount是在组件被拆解或 "解挂 "之前立即调用的。这通常用于清除组件的资源依赖关系,这些依赖关系不会随着组件的卸载而简单地被移除(例如,移除任何与组件相关的setInterval()实例,或者因为组件的存在而在 "文档 "上设置的 "eventListener")。l  render是最重要的生命周期方法,也是任何组件中唯一必须存在的方法。它通常在每次更新组件的状态时都会被调用。 JSXJSX,即JavaScript XML,是对JavaScript语言语法的扩展。JSX在外观上类似于HTML,它提供了一种开发者熟悉的语法结构化组件渲染的方法。React组件通常是使用JSX编写的,尽管不一定非要使用JSX(组件也可以用纯JavaScript编写)。JSX类似于Facebook为PHP创建的另一种名为XHP的扩展语法。 JSX代码的一个例子:class App extends React.Component {  render() {    return (      <div>        <p>Header</p>        <p>Content</p>        <p>Footer</p>      </div>    );  }}  嵌套元素同一层次上的多个元素需要被包裹在一个容器元素中,如上图中的<div>元素。 属性JSX提供了一系列的元素属性,旨在对应HTML提供的属性。这些自定义的属性也可以传递给组件,所有的属性都会被组件作为props接收。 JavaScript表达式JavaScript表达式(但不是语句)可以在JSX内部通过大括号{}使用。<h1>{10+1}</h1> 上面代码的显示结果是:<h1>11</h1> 条件语句If-else语句不能在JSX中使用,但可以使用条件表达式来代替。下面的例子当i为1时将 { i === 1 ? 'true' : 'false' } 呈现为字符串 'true'。 class App extends React.Component {  render() {    const i = 1;    return (      <div>        <h1>{ i === 1 ? 'true' : 'false' }</h1>      </div>    );  }} 结果会是:<div>  <h1>true</h1></div> 函数和JSX可以用于条件表达式中:class App extends React.Component {  render() {    const sections = [1, 2, 3];    return (      <div>        {sections.length > 0 && sections.map(n => (            /* 'key'必须唯一 */            <div key={"section-" + n}>Section {n}</div>        ))}      </div>    );  }} 结果会是:  <div>  <div>Section 1</div>  <div>Section 2</div>  <div>Section 3</div></div> 用JSX编写的代码需要被Babel等工具进行转换以后才能被Web浏览器所理解,这种处理一般是在软件构建过程中进行的,然后再部署构建后的应用程序。 超越HTML的架构React的基本架构不仅仅适用于在浏览器中渲染HTML。例如,Facebook有动态图表,可以渲染到<canvas>标签,而Netflix和PayPal使用通用加载,在服务器和客户端上渲染相同的HTML。 React HooksHooks是让开发者从函数组件中 "钩入"React状态和生命周期特性的函数。它们使代码具有更强的可读性且更易理解。Hooks并不在类组件内工作,它的终极目标是在React中消除类组件的存在。 React提供了一些内置的Hooks,如useState、useContext、useReducer和useEffect等。它们都在Hooks API参考书中做了说明。使用最多的是useState和useEffect,分别在React组件中控制状态和检测状态变化。  Hooks规则 Hooks也有一些规则,在使用Hooks之前必须遵循这些规则: l  钩子只能在顶层调用(不能在循环或if语句中调用)。l  钩子只能在React函数组件中调用,不能在普通函数或类组件中调用。 定制Hooks构建自己的Hooks,也就是所谓的自定义Hooks,可以让你把组件逻辑提取到可重用的函数中。自定义钩子是一个名称以 "use "开头的JavaScript函数,它可以调用其他的钩子。钩子的规则也适用于它们。 常用术语 React并没有试图提供一个完整的 "应用程序库"。它是专门为构建用户界面而设计的,因此并不包括许多一些开发者认为构建应用程序所需的工具。这使得开发者可以选择任何一个库来完成诸如执行网络访问或本地数据存储等任务。这种情况也就决定了React技术在创建网页应用时标准无法统一。 Flux架构的使用为了支持React的单向数据流的概念(与AngularJS/Angular的双向数据流形成对比),Flux架构是流行的模型-视图-控制器(MVC)架构的具有代表性的替代方案。Flux的特点是,数据动作通过中央调度器发送到一个存储仓库,而对存储仓库数据的变化会被传送回视图。当与React一起使用时,这种传送是通过组件属性完成的。 Flux可以被认为是观察者模式的一个变种。 Flux架构下的React组件不应该直接修改传递给它的任何props,而是应该传递回调函数,这些回调函数可以创建由调度器发送的数据动作来修改存储仓库。数据动作是一个对象,其职责是描述已经发生的事情:例如,一个数据动作描述的是一个用户 "follow"另一个用户。它可能包含如下数据:用户ID,目标用户ID,以及USER_FOLLOWED_ANOTHER_USER枚举类型。存储仓库,是一个数据模型,可以根据从调度器接收到的数据动作来改变自己。这种模式有时被表述为 "属性向下流动,数据动作向上流动"。自Flux诞生以来,Flux的许多实现被创造出来,其中最著名的是Redux,它的特点是单一的存储仓库,通常被称为单一的数据真相源。 历史React是由Facebook的软件工程师Jordan Walke创建的,受PHP的HTML组件库XHP的启发,发布了React的早期原型,名为 "FaxJS",。它于2011年首次部署在Facebook的News Feed上,后来于2012年部署在Instagram上。2013年5月在美国JSConf大会上开源。 React Native是2015年2月在Facebook的React Conf上宣布的,2015年3月开源的React Native,实现了原生的Android、iOS和UWP开发。 2017年4月18日,Facebook宣布了React Fiber,这是React库的一个新的核心算法,用于构建用户界面,React Fiber将成为React库未来任何改进和功能开发的基础。 2017年9月26日,React 16.0正式对外发布。 2019年2月16日,React 16.8正式对外发布,该版本引入了React Hooks。 常用命令 创建工程:npx create-react-app my-app开发环境运行:npm start 生产环境打包:npm run build 【官方网站】http://reactjs.org/ 【最新版本】16.13.1于2020年3月19日【授权】MIT License  【Angular】Angular(通常被称为 "Angular 2+"或 "Angular v2及以上版本")是一个基于TypeScript的开源Web应用框架,由Google的Angular团队和由个人以及企业组成的社区领导。 Angular是由构建AngularJS的同一个团队从零开始重写的。 Angular和AngularJS的区别 l  Angular没有 "Scope"或控制器的概念,相反,它使用组件的层次结构作为其主要的架构特征。l  Angular有不同的表达式语法,重点是"[]"用于属性绑定,"() "用于事件绑定l  模块化 - 许多核心功能已转移到模块上l  Angular推荐使用微软的TypeScript语言,它引入了以下特性。n  静态键入,包括Genericsn  注解l  TypeScript是ECMAScript 6 (ES6)的超集,向后兼容ECMAScript 5(即:JavaScript)。l  动态加载l  异步模板编译l  由RxJS提供的迭代回调。RxJS限制了状态的可见性和调试,但这些问题可以通过像ngReact或ngrx这样的反应式附加组件来解决。l  支持Angular Universal,可以在服务器上运行Angular应用程序。 历史命名最初,AngularJS的重写被称为 "Angular 2",但这导致了开发人员的迷糊。为了澄清,团队宣布,每个框架使用不同的术语,其中 "AngularJS "指的是1.X版本, "Angular " 指的是2及以上版本。 版本2Angular 2.0在2014年10月22-23日的ng-Europe大会上宣布。2.0版本的剧烈变化在开发者中引起了相当大的争议。 2015年4月30日,Angular开发者宣布Angular 2从Alpha转为开发者预览版,2015年12月Angular 2转为Beta版,2016年5月发布了第一个发布候选版本,2016年9月14日发布了最终版本。 版本42016年12月13日Angular 4发布,跳过了3,避免了因路由器包的版本错位导致的混乱,当时已经发布的版本为v3.3.0。最终版本于2017年3月23日发布,Angular 4向后兼容Angular 2。 Angular 4.3版本是一个小版本,它是4.x.x版本的替换版本。 4.3版本的功能 l  介绍了HttpClient,一个更小、更容易使用、更强大的HTTP请求库。l  为守护者和解析器提供了新的路由器生命周期事件。四个新事件。GuardsCheckStart、GuardsCheckEnd、ResolveStart、ResolveEnd加入了现有的NavigationStart等生命周期事件集。l  有条件地禁用动画。  版本5Angular 5于2017年11月1日发布,Angular 5的主要改进包括支持渐进式Web应用、构建优化器以及与Material Design相关的改进。版本6Angular 6于2018年5月4日发布。这个版本,关注的重点不在于底层框架,更多的是工具链,以及让Angular在未来的更新和升级更加容易,比如:ngupdate、ng add、Angular元素、Angular Material+CDK组件、Angular Material入门组件、CLI工作区、库支持、树形摇动提供者、动画性能提升、RxJS v6。版本7Angular 7已于2018年10月18日发布。更新内容涉及到应用性能、Angular Material & CDK、虚拟滚动、Selects的可访问性改进、现在支持自定义元素使用Web标准的内容投影,以及关于Typescript 3.1、RxJS 6.3、Node 10(仍支持Node 8)的依赖性更新。版本8Angular 8已于2019年5月28日发布。具有所有应用代码的差异化加载、惰性路由的动态导入、Web工作者、TypeScript 3.4支持、以及Angular Ivy作为预览版可配置使用。Angular Ivy预览包括: l  生成的代码,在运行时更容易阅读和调试。l  更快的重建时间l  减少有效载荷l  改进了模板类型检查l  向后兼容 版本9Angular 9已于2020年2月6日发布。第9版在默认情况下使用Ivy编译器。Angular可以与TypeScript 3.6和3.7兼容。除了数百个bug修复之外,Ivy编译器和运行时还提供了许多优势: l  更小的软件包l  更快的测试l  更好的调试l  改进的CSS类和样式绑定l  改进的类型检查l  改善了构建错误l  改善了构建时间,默认开启AOT功能l  提高国际化功能 特点组件化一个组件例子Html部分 <h2>Products</h2> <div *ngFor="let product of products">   <h3>      {{ product.name }}  </h3> </div>  Typescript部分export class ProductListComponent {  products = products;}  路由 @NgModule({  imports: [    BrowserModule,    ReactiveFormsModule,    RouterModule.forRoot([      { path: '', component: ProductListComponent },      { path: 'products/:productId', component: ProductDetailsComponent },    ])  ], 数据管理定义服务类export class CartService {  items = [];   constructor(    private http: HttpClient  ) {}   addToCart(product) {    this.items.push(product);  }   getItems() {    return this.items;  }   clearCart() {    this.items = [];    return this.items;  }   getShippingPrices() {    return this.http.get('/assets/shipping.json');  }}调用服务类export class ShippingComponent implements OnInit {  shippingCosts;   constructor(    private cartService: CartService  ) {  }   ngOnInit() {    this.shippingCosts = this.cartService.getShippingPrices();  } }常用命令 从终端上,全局安装Angular CLI:npm install -g @angular/cli 使用 ng new 命令创建一个新的 Angular CLI 工作区:ng new my-project-name 开发环境运行:ng serve 生产环境打包:ng build --prod 【官方网站】https://angular.io/ 【最新版本】9.1.2于2020年4月15日【授权】MIT License  【Vue】 Vue.js(通常被称为Vue;发音为/vjuː/,类似于 "view")是一个开源的Model-view-viewmodel JavaScript框架,用于构建用户界面和单页面应用程序。它由Evan You创建,由他和来自Netlify和Netguru等多家公司的核心成员维护。 概述 Vue.js的特点是,它采用了一个渐进式的架构,专注于声明式渲染和组件合成。复杂应用所需的高级功能,如路由、状态管理和构建工具等,都是通过官方维护的支持库和包提供的,其中Nuxt.js是最受欢迎的解决方案之一。 Vue.js可以让你用称为指令(directives)的HTML属性来扩展HTML。 历史Vue是由Evan You创建的。在Google工作期间,他使用AngularJS技术参与了多个项目的开发的,之后创建了Vue。他后来总结了自己的思考过程。"我想,如果我可以把AngularJS真正优秀的部分提取出来,然后构建一些轻量级的东西,会怎么样呢?" 项目的第一个版本源码提交日期是2013年7月,Vue在2014年2月首次发布。 特点组件化Vue 组件扩展了基本的 HTML 元素来封装可重用的代码。从高层次的角度看,组件是Vue编译器附加行为的自定义元素。在Vue中,组件本质上就是一个带有预设选项的Vue实例。下面的代码片段包含了一个Vue组件的例子。该组件显示了一个按钮,并打印出按钮被点击的次数。 <div id="tuto">    <button-clicked v-bind:initial-count="0"></button-clicked></div> <script>Vue.component('button-clicked', {  props: [ "initialCount" ],  data: () => ({    count: 0,  }),  template: `<button v-on:click="onClick">Clicked {{ count }} times</button>`,  computed: {    countTimesTwo() {      return this.count * 2;    }  },  watch: {    count(newValue, oldValue) {      console.log(`The value of count is changed from ${oldValue} to ${newValue}.`);    }  },  methods: {    onClick() {        this.count += 1;    }  },  mounted() {    this.count = this.initialCount;  }}); new Vue({  el: '#tuto',});</script> 模板Vue使用基于HTML的模板语法,允许将渲染的DOM绑定到Vue实例的底层数据。所有 Vue 模板都是有效的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。Vue 将模板编译成虚拟 DOM 渲染函数。虚拟文档对象模型(或 "DOM")允许Vue在更新浏览器之前在其内存中渲染组件。结合反应式系统,Vue能够计算出需要重新渲染的组件的最小数量,并在App状态发生变化时,启动最小量的DOM操作。 Vue用户可以使用模板语法,也可以选择使用JSX直接编写渲染函数,渲染函数允许从软件组件中构建应用程序。 反应式系统Vue的特点是采用了反应式系统,它使用纯JavaScript对象和优化的重渲染。每个组件在渲染过程中都会跟踪其反应式的依赖关系,因此系统可以精确地知道什么时候重新渲染,以及哪些组件需要重新渲染。 变换效果当从DOM中插入、更新或删除项目时,Vue提供了多种方法来部署变换效果。这包括了以下工具: l  自动应用CSS变换和动画的类l  集成第三方CSS动画库,如Animate.css等。l  在变换hooks期间,使用JavaScript直接操作DOM。l  集成第三方JavaScript动画库,如Velocity.js等。 当在变换组件中的元素**入或移除时,会出现这样的情况:l  Vue会自动检测到目标元素是否应用了CSS变换或动画。如果有,CSS变换类将在适当的时间添加/删除。l  如果变换组件提供了JavaScript hooks,这些hooks将在适当的时间被调用。l  如果没有检测到CSS变换/动画,并且没有提供JavaScript hooks,那么插入和/或移除的DOM操作将在下一帧中立即执行。  路由单页面应用程序(SPA)的一个传统缺点是无法分享到特定网页中的确切 "子 "页面的链接。由于SPA只向用户提供一个基于URL的服务器响应(它通常服务于index.html或index.vue),因此通常情况下,将某些屏幕作为书签或分享到特定部分的链接是很困难的,甚至是不可能的。为了解决这个问题,许多客户端路由器用 "hashbang"(#!)来划分动态URL,例如page.com/#!/。然而,在HTML5中,大多数现代浏览器都支持不使用hashbang的路由。 Vue提供了一个界面,可以根据当前的URL路径来改变页面上显示的内容 – 可以有多种方式(无论是通过电子邮件链接、刷新还是页面内链接)。此外,当某些浏览器事件(如点击)发生在按钮或链接上时,使用前端路由器可以有意识地转换浏览器路径。Vue本身并没有自带前端路由。但开源的 "vue-router "包提供了一个API来更新应用程序的URL,支持返回按钮(导航历史记录),并支持电子邮件密码重置或电子邮件验证链接的认证URL参数。它支持将嵌套路由映射到嵌套组件,并提供精细化的过渡控制。添加了vue-router后,组件只需映射到它们所属的路由,父/根路由必须指明子路由的渲染位置。 <div id="app">  <router-view></router-view></div>... <script>...const User = {  template: '<div>User {{ $route.params.id }}</div>'} const router = new VueRouter({  routes: [    { path: '/user/:id', component: User }  ]})...</script> 上面的代码: l  在websitename.com/user/<id>中设置一个前端路径。l  这将在(const User...)中定义的User组件中呈现。l  允许用户组件使用$route对象的params键输入用户的特定ID:$route.params.id。l  这个模板(根据传递到路由器中的参数变化)将被渲染到DOM的div#app里面的<router-view></router-view>。l  最后生成的HTML将是:websitename.com/user/1: <div id="app">  <div>    <div>User 1</div>  </div></div> 生态系统核心库自带的工具和库都是由核心团队和贡献者开发的。 官方工具l  Devtools - 用于调试Vue.js应用程序的浏览器devtools扩展。l  Vue CLI - 用于快速开发Vue.js的标准工具书l  Vue Loader - 一个webpack加载器,允许以单文件组件(SFCs)的格式编写Vue组件。 官方程序库 l  Vue Router - Vue.js的官方路由器l  Vuex – 基于 Flux模式的 Vue.js 的集中式状态管理。l  Vue Server Renderer - 用于 Vue.js 的服务器端渲染。 常用命令安装工具npm install -g @vue/cli 创建工程:vue create my-project 开发环境运行:npm run serve 生产环境打包:npm run build 【官方网站】https://vuejs.org/ 【最新版本】2.6.1于2019年12月13日【授权】MIT License                           【小结】本文对于当前顶级的前端技术做了较为详尽的探索,前端技术一个大的方向是单页应用 (SPA,Single Page Application),我们在选取针对本业务的前端技术时需要结合如下几个方面来考虑: 1.     成员当前技能,这是一个很现实的问题,大多数程序员会选择自己比较熟悉的技术。这里要思考一下,目前自己熟悉的技术是不是最优选项?2.     可利用的学习时间,如果发现要使用的技术需要一些时间学习,这个时间的开销到底会不会与开发进度有冲突?3.     能否保证项目的复杂度最低,这个是比较关键的因素。先进技术之所以先进就是因为可以让开发者把时间和精力放在真正的业务开发上面来,如果要使用的技术需要进行很多与业务不相关的配置,就需要问一个问题,有没有更好的办法? 最后,希望本文对现有或者以后的业务开发有指导或者借鉴作用。
  • [分享交流] angular 怎么和echart配,有例子么
    RT
  • Angularjs1.X开发交流分享-How to rebuild your god-like controller?
    2018年12月13日的前端开发内部培训,分享关于如何重构controller的一些经验,以及代码结构规划的一些建议,欢迎交流指点。Angularjs培训-如何重构controller.pdf( 预览 )安利一下自己的大前端技术博客,欢迎互粉。博客园:http://www.cnblogs.com/dashnowords华为云社区:https://bbs.huaweicloud.com/community/iblognew/id_1524611247318830
  • [干货分享] Angular修炼秘籍之易筋经篇
    今年1月份开始接触angular的新版本-- Angular5,之前一直是用的angular1.x。开始了angular5的踩坑之路,话不多说进入正题吧… 一、初识angular5。学习angualr5的文章很多, 推荐博文:https://segmentfault.com/a/1190000008754631。二、刚开始学习遇到的问题。 1.打包之后的文件运行问题 直接打开index.html 页面无法打开,控制台会报如下的错误: 是因为angular是基于npm环境搭建的,打包后的文件需要在模拟服务器上打开。解决方案:可以全局安装npm的http-server模块。构建本地web模拟服务器。 安装方法:在cmd窗口执行命令:npm install http-server –g 使用方法:在index所在文件夹执行cmd窗口命令:hs –p 端口号 –o 即可打开运行。 2.多级路由嵌套及懒加载问题​如果项目当中页面嵌套过多的情况,会出现路由嵌套的情况。 多级路由嵌套两种写法:1).直接进行路由嵌套如下:{ path: 'dashboard', component: DashboardComponent, canDeactivate: [CanLeaveDetail], children: [ { path: 'partA', component: DashboardPartAComponent }, { path: 'partB', component: DashboardPartBComponent }, { path: 'partC', component: DashboardPartCComponent } ]}2).懒加载模式: 主路由:{ path: 'lifeCycle', canActivate: [AuthService], loadChildren: './LifecycleHooks/lifeCycleHooks.module#LifeCycleHooksModule' }子路由:{ path: '', // 使用loadChildren方式此处路径为空 component: LifeCycleHooksComponent, children: [ { path: '', redirectTo: 'componentLC', pathMatch: 'full'}, { path: 'componentLC', component:ComponentLCComponent } ]}子路由全部用loadChildren加载,到这个路由的时候在加载路由下的模块及组件。loadChildren格式:要加载的模块的相对地址#要加载的模块名称。注意:子路由中的第一个path一定要为空。 三、开发组件过程中遇到的一些问题。 1.使用组件的时候直接加在组件上的样式和样式类无效: 解决方案:给组件父容器上加样式或样式类,代码如下:基本用法[value]="value" [maxValue]="maxValue">2. angular样式封装 组件内部定义的样式类不影响组件外部。采用的策略是:angular在解析组件内部写的样式会加上 _ngcontent-属性。这样不会全局污染。打开控制台,查看DOM接口,在head标签中的style标签中会看到如下样式:查看宿主元素也就是自定义的组件标签DOM,如下图所示会加上_nghost-属性。 导致的问题是组件内部定义的样式类在宿主元素上不生效。为解决上述为题angualr提供了三种样式封装策略。需要引入ViewEncapsulation。import { ViewEncapsulation } from '@angular/core'具体方案:在元数据上加encapsulation属性。有下面三个值:· ViewEncapsulation. Emulated(仿真)这是默认选项,通过Angular来模拟类似Shadow DOM的行为。不会影响全局样式。· ViewEncapsulation. Native(原生)原先浏览器Shadow DOM行为。· ViewEncapsulation.None(无)angular无任何封装行为,会全局污染。 3.如何给一个DOM节点上使用2个ng-class。 在angular1.x上只能有一个,并且只有一个生效。Angular5中有个替代方案如下:[class.className]=”expression”可以写多个。[ngClass]="progressClass">4.如何实现接口的双向数据绑定(Two-Way Data Binding) 双向数据绑定原理图: 双向绑定是由两个单向绑定组成:· 模型 -> 视图数据绑定· 视图 -> 模型事件绑定 Angular 2.x及以上版本 中 [] 实现了模型到视图的数据绑定,() 实现了视图到模型的事件绑定。两个结合在一起 [()] 就实现了双向绑定。就好比[(ngModel)]。 1).在angular1.x中可以按照下面的方式实现接口的数据绑定,通过 ‘=’修饰符表示这个数据双向绑定如下代码,定义接口的时候使用‘=’scope: { format: ‘=?’}2) 在angular2.x以上的版本实现方式有下面两种2.1) Input和output装饰器结合使用 实现方案:I.定义一个Input输入属性,定义一个Output输出的事件接口,名称之间形式如下:@Input() stated : boolean;@Output() statedChange = new EventEmitter();II.在内部修改stated的时候把这个值,把这个值通知到外部。private toggleSwitch(): void { this.stated = !this.stated; this.statedChange.emit(this.stated);}III.使用的时候形式如下原理如下:[(stated)] 可以拆分成两部分stated和statedChange ,[stated] 用于绑定输入属性,(statedChange) 用于绑定输出属性。当 Angular 在解析模板时,遇到[(stated)]形式的绑定语法,它会期待这个指令中会存在一个名为 stated 的输入属性和一个名为 statedChange 的输出属性。 2.2) 创建类表单组件 推荐学习地址:https://segmentfault.com/a/1190000009070500把这个组件当中最重要的接口定义成双向数据绑定,如下方式使用:双向数据绑定的值用[(ngModel)]=”xxx”绑定实现.使用形式如下(就好比原生的textarea标签) 5. 测试用例定义的[id]变量,在组件的ngOnInit()生命周期钩子拿不到。解决方法:在ngAfterViewInit()生命周期钩子可以拿到。 用户写宿主id三种方式: 1)给HTML属性id赋字符串或者变量2)给HTML属性id赋变量 />3)给DOM属性id赋变量 />组件开发读写宿主id的三种方法: 1)读HTML属性idconstructor(@Attribute('id') public hostId ) {}2)写HTML属性id @HostBinding('attr.id') hostId: string;写DOM属性id@HostBinding('id') hostId: string;3)读写DOM属性idconstructor(private hostRef: ElementRef ) { }ngAfterViewInit() { const id = this.hostRef.nativeElement.id}6.函数作为入参时候,函数尽量写成箭头函数形式。 组件开发过程过需要修改或者读取组件属性或者方法时候,一定要写成箭头函数形式,不然会出现this指向问题。比较常见的2种应用。1)定时器:let timer = setInterval(() => { startDist += motion.step * dir; topy += motion.step * dir; if (Math.abs(startDist - startPoint) >= motion.distance) { clearInterval(timer); timer = null; }}, 1); 2)事件监听:document.addEventListener('mousemove', this.mouseMoveHandlerFn);private mouseMoveHandlerFn = ($event) => { const option = this.config.options;}7.组件通讯之子获取父的实例 应用场景:组件嵌套,子组件需要用到父组件的属性和方法。 组件嵌套形式如下面代码: 父组件的模板代码如下,父组件嵌套了子组件app-child.方案:在constructor(构造函数)中使用@Inject()依赖注入父组件。如下面代码:export class TiBtnItemComponent { constructor( @Inject(TiBtnRadioCompontent) private btnRadio: TiBtnRadioCompontent ) {}} 并且上面的方式是单例的(父组件下不管有几个子组件,父组件只实例化一次),但是这种方式只能注入一个组件实例。在实际开发中,例如选择按钮组有多选按钮和单选按钮,这个时候就需要注入多个父组件。用上面同样的方式同时注入两个就会报如下错误:原因如下:当组件申请一个依赖时,Angular从该组件本身的注入器开始,沿着依赖注入器的树往上找,直到找到第一个符合要求的提供商。如果找不到就会抛出一个错误。 解决方案:在@Inject()装饰前加@Optional(),如下面代码:export class TiBtnItemComponent { constructor( @Optional() @Inject(TiBtnRadioCompontent) private btnRadio: TiBtnRadioCompontent, @Optional() @Inject(TiBtnCheckboxCompontent) private btnRadio: TiBtnCheckboxCompontent ) {}} 解决原因:当Angular找不到依赖时,@Optional装饰器会告诉Angular继续执行,Angualr会把此注入参数设置为null(而不是默认的抛出错误的行为)。 8. 组件生命周期总结如上图,为组件初始化各个生命周期执行顺序。其中紫色的生命周期只在组件初始化执行一次,绿色的会执行多次。1)ngOnChanges频率较低,仅监听用户对@Input变量的改变。注意:监听不到组件开发者在代码中对变量更改。可以考虑使用@Input set访问器方式,监听变量,所有改变都可以监听到。 2)ngDoCheck,触发频率超高。angular自身监视[ngclass]数组变化,是放在这个生命周期。使用了IterableDiffers机制。 其他什么情况适合放在这里,暂时不太清楚。 3)ngAfterContentChecked,ngAfterViewChecked相对频率低。我们目前代码监听Dom属性变化,暂时放在这里了。不知道有没有更好办法监听Dom属性变化。注意:如果在ngAfterViewChecked里面,改变了模板中使用的变量,此变量不一定能及时更新到试图。需要强制刷新this.changeDetectorRef.detectChanges(); 4)想监听数组和对象属性变化,angular内部使用了IterableDiffers和KeyValueDiffer。参考[ngClass]源码。
  • [干货分享] 快速入门!详解Angular2路由的分类及使用!
    路由是 Angular 应用程序的核心,它加载与所请求路由相关联的组件,以及获取特定路由的相关数据。Angular 的路由器主要功能是让用户从一个视图导航到另一个视图。 五个基础路由对象 学习路由之前首先来了解一下这些基础的路由对象,下边将详细介绍各自的用法。 1. Routes:路由配置,保存着哪个 url 对应展示哪个组件,以及在哪个 routerOuterlet 中展示组件; 2. RouterOutlet:路由插座,在html中标记路由内容呈现位置的占位符指令; 3. routerLink:在html中使用链接方式实现页面导航; 4. Router:在ts文件中负责跳转路由操作,可以通过调用其 navigate()和navigateByUrl() 方法来导航到一个指定的路由; 5. ActivedRoute:当前激活的路由对象,保存着当前路由的信息,如路由地址、路由参数等; 创建路由模块,配置路由 首先,通过命令行创建一个带有路由模块的应用:ng new projectName –routing;再通过命令行创建两个组件:ng g component main; ng g componentproduct; 1. 打开项目中app文件中的app-routing.module.ts 文件,此文件为整个应用的路由模块; 可以在 Routes 中配置路由;routes 参数: path:url 地址 ,component:组件(还有其他参数,具体可看官网介绍); 下边示例中配置了四个路由分别为:默认路由,主页面路由,商品详情页面以及404页面; 注意:路由的定义顺序是刻意如此设计,配置路由时必须将能精准匹配到的路径放前边,将404页面放在最下边; 原因:路由会先从上面匹配,如果匹配不成功就会往下匹配 2. 路由插座 在 html 中标记挂载路由的占位容器,用来盛放加载的组件。 路由导航跳转的方式 通过上边的配置,可以通过手动修改路径来查看个页面路由是否设置成功,实际项目中我们不会去手动修改路径来导航到需要的页面,这时候就需要点击链接或者点击按钮等来实现页面的导航;下边就说一下通过链接和绑定事件的方式来实现路由跳转: 1. 通过页面链接的方式实现路由导航 routerLink: 2. 通过 ts 事件的方式实现路由导航 router 对象: 重定向路由 作用:当用户访问一个特定的路径时,将其重定向的另一个指定的地址;比如维护的官网,业务需求现将新的网址变更,但是希望用户输入旧网址能跳转到新页面,此时就可使用重定向路由;配置重定向路由: {path:'oldSrc',redirectTo:'/'newSrc',pathMatch:'full'}; 在路由时传递参数 第一步:配置接收方路由模块中的 path 属性:{path:'product/:id',component:ProductComponent},第二步:发送参数:this.myRouter.navigateByUrl('/ product/1')跳转到商品详情页面 第三步:接收参数引入 ActivatedRoute 对象:import { ActivatedRoute } from '@angular/router';声明 ActivatedRoute 类型变量:constructor(private routeInfo:ActivatedRoute) { }获取参数:this.productId = this.routeInfo.snapshot.params["id"]; 子路由 通过 children 属性来配置子路由,除了配置路由时有区别其他类似; 子路由的入口: 辅助路由 一个页面中可以同时定义多个插座,并且可以同时控制每个插座上显示的内容。 1. 辅助路由的路由插座定义和主路由一样,只是辅助路由比主路由多了一个 name 属性:用来指定辅助路由显示那几个组件。 2. 配置辅助路由路径:除了 path 和 component 属性外必须添加一个 outlet 属性,用来指定该路由显示在名字叫什么的插座上; 3. 配置入口参数:辅助路由的参数是一个对象,这个对象里面有一个属性 outlets,这个属性的值也是一个对象,该对象里面传一个 name 属性指定要显示的辅助路由的名字,值是该辅助路由需要显示的组件路径; 点击开始聊天,名字叫aux的辅助路由将显示路径为 chat 的组件;点击取消聊天,也就是不希望辅助路由显示的时候将 name 设置为 null。
  • [干货分享] 细说Typescript中的装饰器
    学习Angular2的过程中,很多特性都依靠装饰器来实现,当你看到装饰器这个概念的时候,可能会把其跟设计模式里的Decorator搞混,其实这是完全不同的两个东西。 什么是装饰器? 虽然很像,他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦,不信你可以看看维基上的词条Decorator pattarn里的UML图和那些代码,不烧死一堆脑细胞才怪。 目前为止Javascript中的装饰器还处于建议征集的第二阶段,在不远的ES7中大家能有幸体验一把了。Angular2最初是使用Google自己搞的AtScript语言编写的(牛逼哄哄的感觉,我要写框架了,先搞出来一种语言吧……),但是在 NG-Conf 2015 上,Angular 团队宣布与 TypeScript 团队进行合作(原因不详),但TypeScript需要引入一个类Annotation 特性的语法(及其扩展),此外为了能更好地支持 Angular,TypeScript 引入的装饰器特性并不是纯粹的装饰器提案,附加了一部分特性: [*]支持通过 emitDecoratorMetadata 选项暴露类构造函数的参数类型; [*]支持针对类构造器/方法参数的装饰器; [*]支持类成员属性的装饰器。 就这样Angular与Typescript拥抱在一起了。 装饰器的分类 根据被装饰的对象,我们可以将TS中的装饰器分为以下四种: [*]类装饰器 (Class decorators) [*]属性装饰器 (Property decorators) [*]方法装饰器 (Method decorators) [*]参数装饰器 (Parameter decorators) 类装饰器 类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)。 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。 我们来看一个例子: 可以看到,通过类装饰器我们可以对类的原型对象做一定的修改(装饰)。 编译后的源码为:"use strict";var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators) r = (c 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r;};function sealed(constructor) { Object.seal(constructor); Object.seal(constructor.prototype);}var MyClass = (function () { function MyClass() { this.a = 0; this.b = "hello"; } MyClass = __decorate([ sealed ], MyClass); return MyClass;}());var obj = new MyClass();// obj.c = true; // 编译报错 属性装饰器 属性装饰器声明在一个属性声明之前(紧靠着属性声明)。属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 示例如下: 输出如下: 可以看到,属性装饰器返回的函数会在解释类的对应属性时被调用一次,并可以得到装饰器的参数和被装饰的属性的相关信息。 装饰器方法的调用只会在加载代码时执行一次,调用被装饰的属性不会触发装饰器方法。 方法装饰器 方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。 方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 3. 成员的属性描述符。 示例如下: 输出如下:false, undefined, [object Object], func, {"writable":true,"enumerable":true,"configurable":true}true, this is static, function MyClass() { }, sFunc, {"writable":true,"enumerable":true,"configurable":true}call static methodcall static methodcall methodcall method 可以看到,方法装饰器返回的函数会在解释类的对应方法时被调用一次,并可以得到装饰器的参数和被装饰的方法的相关信息。 装饰器方法的调用只会在加载代码时执行一次,调用被装饰的方法不会触发装饰器方法。 参数装饰器 参数装饰器声明在一个参数声明之前(紧靠着参数声明)。参数装饰器应用于类构造函数或方法声明。参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如 declare的类)里。 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数: 1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 2. 成员的名字。 3. 参数在函数参数列表中的索引。 示例如下: 输出结果为:1 [object Object], func, 22 [object Object], func, 13 [object Object], func, 04 call method5 call method 可以看到,参数装饰器返回的函数会在解释方法的参数时被调用一次,并可以得到参数的相关信息。我们有3个参数就调用了3次。 装饰器方法的调用只会在加载代码时执行一次,调用被装饰的参数的方法不会触发装饰器方法。 Angular中的装饰器 理解了Typescript中的装饰器,我们回头再看Angular中的装饰器就不会感觉到鸭梨了,Angular中内置的装饰器如下: [*]类装饰器:@Component、@NgModule、@Pipe、@Injectable [*]属性装饰器:@Input、@Output、@ContentChild、@ContentChildren、@ViewChild、@ViewChildren [*]方法装饰器:@HostListener [*]参数装饰器:@Inject、@Optional、@Self、@SkipSelf、@Host 这些后续慢慢挖吧。 — END — 别忘了给 作者: 刘赵强 点赞呦! 有什么想对作者说的吗?快来留言吧!长按二维码关注“THINK” ​