<p>This article mainly introduces the in-depth analysis of <a href="http://www.php.cn/course/47.html" target="_blank">angularjs</a>1. Don’t find it difficult. As long as you don’t find it difficult, then you can learn angularjs here. This is a test for everyone. article, let us take a look at this article now</p>
<h2>angular 1 We must also component-oriented programming</h2>
<p>Front-end componentization is an irreversible trend in the front-end development model. The three main front-end Framework <code>angular 2</code> <code>react</code> <code>vue</code> all regard component programming as one of their major selling points, <code>angular 1</code> as a framework with a relatively long history The framework, driven by the illegitimate son <code>angular 2</code>, has finally caught up with the last train of component-based programming. Those old projects in the company finally have the opportunity to experience the taste of component-based programming. </p>
<h2>The road to componentization in angular 1</h2>
<p><code>angular 1</code> Programming ideas similar to componentization have actually been around for a long time, but they were not called components at that time. Directive, after specifying <code>restrict: 'E'</code>, this directive is very similar to the usage of today's components. In <code>angular 1.5</code>, the instructions were restricted based on similar concepts in <code>angular 2</code>, and were reborn into today's components. </p>
<h2>Features of components</h2>
<p>The official documentation lists the differences between components and instructions. In addition, a standard component should also meet the following characteristics. </p>
<ol class=" list-paddingleft-2">
<li><p>The label name of the component must contain a hyphen</p></li>
<li><p>The component has a good life cycle</p></li>
<li><p>Components are self-contained</p></li>
<li><p>Components are self-enclosed</p></li>
<li><p>Components are reusable</p></li>
<li> <p>Components can be customized</p>
</li>
</ol>
<p>The following are explained in sequence. </p>
<h3>Name specifications for components</h3>
<p>Unlike directives, components must be an element, and HTML has special specifications for this. </p>
<p>The HTML specification leaves tags with underscores for developers to use. The elements formed in this way are also called custom elements. Although we have not used the concept of custom elements, the behavior of the two is similar. We should meet this standard. </p>
<p>This specification corresponds to <code>angular 1</code>: the component name must be in camel case. </p>
<p>For example: </p>
<pre class="brush:php;toolbar:false">module.component('dialog', {
// ...
});</pre>
<p>This is not right. The HTML specification has defined the standard element dialog. Reusing tag names may cause our custom component behavior to be mixed with the behavior of the standard element, leading to weird bugs; and if this is done, it will also indirectly prevent developers from using the native <code>dialog</code> tag. </p>
<p>In addition, even if a certain element is not defined in the current standard, it does not mean that it will not be defined in the future. Since our program runs in the browser, it must follow the rules. This is a legal way of writing: </p>
<pre class="brush:php;toolbar:false">module.component('customDialog', {
// ...
});</pre>
<h3>Self-containedness of components</h3>
<p>A well-designed component must have its own behavior and default style. </p>
<h4>Default behavior</h4>
<p>The default behavior is defined with the controller (Controller) in <code>angular 1</code>. </p>
<pre class="brush:php;toolbar:false">function CustomDialogController($service) {
this.someField = 123;
this.someMethod = function someMethod() {
}
}
CustomDialogController.$inject = ['$service'];
module.component('customDialog', {
controller: CustomDialogController,
template: require('./customDialogTemplate.html'),
});</pre>
<p>Because the component enables <code>controllerAs</code> by default, all variables and functions are bound to <code>this</code>, so you can also use <code>ES2015</code> The <code>class</code> syntax to organize the code: </p>
<pre class="brush:php;toolbar:false">class CustomDialogController {
constructor($service) {
}
someMethod() {
}
}
CustomDialogController.$inject = ['$service'];
module.component('customDialog', {
controller: CustomDialogController,
template: require('./customDialogTemplate.html'),
});</pre>
<p>One problem with this is that other functions cannot use the service (Service) injected in <code>constructor</code>, and can only use <code>this</code> One transfer. My personal approach is as follows: </p>
<pre class="brush:php;toolbar:false">class CustomDialogController {
constructor($service) {
this.services = { $service };
}
someMethod() {
const { $service } = this.services;
}
}
// 下略</pre>
<p> It is recommended that controllers of components with relatively simple logic be defined using <code>function</code>, and complex components should be defined using <code>class</code>, with the latter code The levels should be clearer and easier to read. </p>
<h4>Default style</h4>
<p>The default style of the component is specified directly using the style sheet. </p>
<pre class="brush:php;toolbar:false">custom-dialog {
display: block;
// ...
}</pre>
<p>For all tags that the browser does not recognize, the default is inline elements (<code>display: inline</code>), which is usually not desired for components. So custom components usually have at least <code>display: (inline-)block</code> to change the default display mode of elements. </p>
<h3>Self-closure of components</h3>
<p>Self-closure includes two aspects: self-closure of data and self-closure of style. </p>
<h4>Self-enclosure of data</h4>
<p><code>angular 1</code> In angular 1<code>, the component's own scope is already isolated (isolate), that is, the component's scope does not inherit from the parent scope ( </code>__proto__<code> is </code>null</p>). In addition, a canonical component should not directly use external data, because this will destroy the reusability of the component. Here are a few examples: <ol class=" list-paddingleft-2">
<li>
<p></p>$rootScope</li>
<li>
<p></p>$root, $parent (in template)</li>
<li>
<p></p>Routing parameters</li>
<li>
<p></p>localStorage, sessionStorage</li>
</ol>
<p>##These data should be passed in through parameter binding <code>binding</code>. If the component is generated by a routing plug-in, resolve can be used. </p>
<p>Secondly, parameter binding should not use two-way binding <code>=</code>, and standardized components should not (directly) modify data passed in outside the component. There are two officially recommended parameter binding methods</p>
<ol class=" list-paddingleft-2"><li><p><code><</code> 单向绑定,绑定可变数据。通常用于给组件传递数据</p></li><li><p><code>@</code> 字符串绑定,绑定字符串。通常用于指定组件行为</p></li></ol><p>对于单向绑定对象的情况,由于是引用传递,也不应该修改对象内部的属性。</p><p>遇到要向外部传值的情况,推荐使用 ngModel 或 事件绑定(下面会提到)</p><h4>样式的自封闭性</h4><p>组件间的样式不应该互相干扰,这一点可以简单的通过 <code>scss</code> 的样式嵌套(Nesting)实现:</p><pre class="brush:php;toolbar:false">custom-dialog {
display: block;
// ...
.title {
// ...
}
.body {
// ...
}
}</pre><p>这样可以简单的把组件的内置样式表限制在组件内部,从而避免样式外溢。但是这种方法对在组件内部的其他组件不起效果。如果这个组件的模板中还引用了别的组件,或者这个组件被定义为可嵌入的(transclude),那么可以考虑加类名前缀:</p><pre class="brush:php;toolbar:false">custom-dialog {
display: block;
.custom-dialog {
&-title {
// ..
}
&-body {
}
}
}</pre><h3>组件的可复用性</h3><p>组件为复用而生,拥有良好自封闭性的组件必然是可复用的,因为这个组件不受任何外部因素干扰。组件的复用形式包括</p><ol class=" list-paddingleft-2"><li><p>一个页面中使用多次</p></li><li><p>在多个页面中使用</p></li><li><p><code>ng-repeat</code></p></li><li><p>自己套自己(递归树结构)</p></li><li><p>整个源代码拷贝到其他项目中</p></li></ol><p>等等。一个高度可复用的组件则可以被称为控件,是可以单独投稿 <code>npm</code> 项目库的。</p><p>当然,有些组件(比如单独的页面)可能复用需求没那么高,可以视组件的复用程度不同,从组件的自封闭性和整体代码量做一些取舍。</p><h3>组件的定制化</h3><p>一个高度可复用的组件一定可以被定制。</p><h4>行为的定制化</h4><p>通过参数绑定实现组件行为的定制化。例如:</p><pre class="brush:php;toolbar:false"><custom-dialog x-title="My Dialog" x-modal="true"><!-- 与标签名一样,自定义属性名也应该使用中划线 -->
<!--content -->
</custom-dialog><pre class="brush:php;toolbar:false">module.component('customDialog', {
template: require('./customDialogTemplate.html'),
transclude: true,
bindings: {
title: "@",
modal: '<',
},
});</pre><p>出于使用方便的考虑,定制用的参数都是可选的,组件内部实现应该给每个定制参数设定默认值。(想看更多就到PHP中文网<a href="http://www.php.cn/course/47.html" target="_blank">angularjs学习手册</a>中学习)</p><h4>样式的定制化</h4><p>组件风格定制可以使用 class 判断。</p><pre class="brush:php;toolbar:false">custom-dialog {
display: block;
// ...
.title {
font-size: 16px;
// ...
}
&.big {
.title {
font-size: 24px;
}
}
}</pre><p>使用时</p><pre class="brush:php;toolbar:false"><custom-dialog x-title="My Dialog" class="mydialog big"></custom-dialog></pre>
<p>深度定制样式比较好的方式是 CSS 属性(CSS Variable,注意不是 SCSS 属性)。</p>
<pre class="brush:php;toolbar:false">custom-dialog {
display: block;
// ...
.title {
font-size: 16px;
color: var(--dialog-title-color, #333);
// ...
}
&.big {
.title {
font-size: 24px;
}
}
}</pre>
<p>这时只需要文档中说明标题颜色使用 <code>--dialog-title-color</code> 这个 CSS 变量就好,外部使用不依赖于组件内部 DOM 实现。使用时</p>
<pre class="brush:php;toolbar:false">.mydialog {
--dialog-title-color: red;
}</pre>
<h3>组件的生命周期</h3>
<p>从创建至销毁,组件有自己的生命周期(lifecycle),而不像指令那样把 scope 作为生命周期。常用的回调函数如下:</p>
<ul class=" list-paddingleft-2">
<li><p><code>$onInit()</code>:组件被初始化时调用。与 constructor 不同,<code>angular 1</code> 确保 <code>$onInit</code> 被调用时组件的所有参数绑定都被正确赋值。</p></li>
<li><p><code>$onChanges(changeObj)</code>:组件参数绑定值被改变时调用。用于监听绑定值的变化,初次绑定时也会调用这个函数。</p></li>
<li><p><code>$onDestroy()</code>:组件被销毁时调用。用于清理内部资源如 <code>$interval</code> 等。</p></li>
</ul>
<p>这些函数也是绑定在 <code>this</code> 上的。如果 <code>controller</code> 使用 <code>ES2015</code> 的 <code>class</code> 定义方式,可以这么写:</p>
<pre class="brush:php;toolbar:false">class CustomDialogController {
constructor() {}
onInit() {}
onChanges({ prop1, prop2 }) {}
onDestroy() {}
}</pre>
<h2>组件间的通信</h2>
<p>组件间通信是一个让很多人头疼的问题,通常有这样 3 种情况</p>
<h3>子 -> 父</h3>
<p>这种情况有标准的实现方式:事件绑定。例如</p>
<pre class="brush:php;toolbar:false">class CustomDialogController {
close($value) {
this.hide = true;
this.onClose({ $value });
}
}
module.component('customDialog', {
controller: CustomDialogController,
template: require('./customDialogTemplate.html'),
bindings: {
onClose: '&',
},
});</pre>
<p>使用时:</p>
<pre class="brush:php;toolbar:false"><custom-dialog on-close="$ctrl.handleClose(value)"></custom-dialog></pre>
<p>这种方式也可以用于子组件向父组件传值。</p>
<h3>父 -> 子</h3>
<p>用于触发子组件的某个动作。除了改变某个在子组件内部监听变化的绑定参数值外,行之有效的方式就只有事件广播。</p>
<p>子组件先监听某个事件</p>
<pre class="brush:php;toolbar:false">$scope.$on('custom-dialog--close', () => this.close());</pre>
<p>父组件发送广播</p>
<pre class="brush:php;toolbar:false">$scope.$broadcast('custom-dialog--close');</pre>
<p>切记:事件是全局性的。当有组件复用的情况时请使用标识指定接收对象(BUS 模型);另外最好给事件名添加组件前缀。</p>
<h3>同级组件</h3>
<p>请通过父级组件中转</p>
<h3>子 -> 某全局性组件</h3>
<p>这个显示 Notification 时最常用。遇到这种情况时,可以封装服务(Service)。例如:</p>
<pre class="brush:php;toolbar:false">module.component('globalNotification', {
controller: class GlobalNotificationController {
constructor(notificationService) {
notificationService.component = this;
}
show(props) {
// ...
}
}
});
module.factory('notify', function NotifyService() {
return {
warn(msg) {
this.show({ type: 'warn', text: msg });
}
error(msg) {
this.show({ type: 'error', text: msg });
}
}
});</pre>
<p>方案并不完美。如果有更好的建议欢迎提出。</p>
<h2>结语</h2>
<p>有人可能问既然三大前端框架都是组件化的,何必还要在 <code>angular 1</code> 上实现。殊不知 <code>angular 1</code> 的组件诞生的初衷就是为了减少向 <code>angular 2</code> 迁移的难度。机会总是留给有准备的人,哪天老板大发慈悲表示给你把代码重写的时间,你却看着项目里满屏的 <code>$scope.abc = xxx</code> 不知所措,这岂不是悲剧。。。</p>
<p>This article ends here (if you want to see more, go to the PHP Chinese website <a href="http://www.php.cn/course/47.html" target="_blank">angularjs Learning Manual</a>). If you have any questions, you can leave a message below</p>
<p class="comments-box-content"></p></code></p></li></ol>
The above is the detailed content of In-depth analysis of Angularjs1 component programming (with examples included). For more information, please follow other related articles on the PHP Chinese website!