整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

前端工程-从原理到轮子之JS模块化

前端工程-从原理到轮子之JS模块化

前,一个典型的前端项目技术框架的选型主要包括以下三个方面:

  1. JS模块化框架。(Require/Sea/ES6 Module/NEJ)

  2. 前端模板框架。(React/Vue/Regular)

  3. 状态管理框架。(Flux/Redux)

    系列文章将从上面三个方面来介绍相关原理,并且尝试自己造一个简单的轮子。

本篇介绍的是 JS模块化

JS模块化是随着前端技术的发展,前端代码爆炸式增长后,工程化所采取的必然措施。目前模块化的思想分为CommonJS、AMD和CMD。有关三者的区别,大家基本都多少有所了解,而且资料很多,这里就不再赘述。

模块化的核心思想:

  1. 拆分 。将js代码按功能逻辑拆分成多个可复用的js代码文件(模块)。

  2. 加载 。如何将模块进行加载执行和输出。

  3. 注入 。能够将一个js模块的输出注入到另一个js模块中。

  4. 依赖管理 。前端工程模块数量众多,需要来管理模块之间的依赖关系。

根据上面的核心思想,可以看出要设计一个模块化工具框架的关键问题有两个:一个是如何将一个模块执行并可以将结果输出注入到另一个模块中;另一个是,在大型项目中模块之间的依赖关系很复杂,如何使模块按正确的依赖顺序进行注入,这就是依赖管理。

下面以具体的例子来实现一个简单的 基于浏览器端AMD 模块化框架(类似NEJ),对外暴露一个define函数,在回调函数中注入依赖,并返回模块输出。要实现的如下面代码所示。

define([ '/lib/util.js', //绝对路径
'./modal/modal.js', //相对路径
'./modal/modal.html',//文本文件], function(Util, Modal, tpl) { /*
 * 模块逻辑
 */
return Module;
})

1. 模块如何加载和执行

先不考虑一个模块的依赖如何处理。假设一个模块的依赖已经注入,那么如何加载和执行该模块,并输出呢?

在浏览器端,我们可以借助浏览器的 script 标签来实现 JS模块文件 的引入和执行,对于 文本模块文件 则可以直接利用 ajax 请求实现。

具体步骤如下:

  • 第一步,获取 模块文件的绝对路径

    要在浏览器内加载文件,首先要获得对应模块文件的完整网络绝对地址。由于 a标签 的href属性总是会返回绝对路径,也就是说它具有把相对路径转成绝对路径的能力,所以这里可以利用该特性来获取模块的绝对网络路径。需要指出的是,对于使用相对路径的依赖模块文件,还需要递归先获取当前模块的网络绝对地址,然后和相对路径拼接成完整的绝对地址。代码如下:

var a=document.createElement('a');
a.id='_defineAbsoluteUrl_';
a.style.display='none';document.body.appendChild(a);function getModuleAbsoluteUrl(path) {
a.href=path; return a.href;
}function parseAbsoluteUrl(url, parentDir) { var relativePrefix='.',
parentPrefix='..',
result; if (parentDir && url.indexOf(relativePrefix)===0) { // 以'./'开头的相对路径
return getModuleAbsoluteUrl(parentDir.replace(/[^\/]*$/, '') + url);
} if (parentDir && url.indexOf(parentPrefix)===0) { // 以'../'开头的相对路径
return getModuleAbsoluteUrl(parentDir.replace(/[\/]*$/, '').replace(/[\/]$/, '').replace(/[^\/]*$/, '') + url);
} return getModuleAbsoluteUrl(url);
}
  • 第二步, 加载和执行模块文件

对于JS文件,利用 script 标签实现。代码如下:

var head=document.getElementsByTagName('head')[0] || document.body;

function loadJsModule(url) { 
 var script=document.createElement('script');
script.charset='utf-8';
script.type='text/javascript';
script.onload=script.onreadystatechange=function() { if (!this.readyState || this.readyState==='loaded' || this.readyState==='complete') { /*
 * 加载逻辑, callback为define的回调函数, args为所有依赖模块的数组
 * callback.apply(window, args);
 */
script.onload=script.onreadystatechange=null;
} 
};
}

对于文本文件,直接用 ajax 实现。代码如下:

var xhr=window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),
textContent='';
xhr.onreadystatechange=function(){ var DONE=4, OK=200; if(xhr.readyState===DONE){ if(xhr.status===OK){
textContent=xhr.responseText; // 返回的文本文件
} else{ console.log("Error: "+ xhr.status); // 加载失败
}
}
}
xhr.open('GET', url, true);// url为文本文件的绝对路径xhr.send(null);

2. 模块依赖管理

一个模块的加载过程如下图所示。

  • 状态管理

    从上面可以看出,一个模块的加载可能存在以下几种可能的状态。

  1. 加载(load)状态,包括未加载(preload)状态、加载(loading)状态和加载完毕(loaded)状态。

  2. 正在加载依赖(pending)状态。

  3. 模块回调完成(finished)状态。

    因此,需要为每个加载的模块加上状态标志(status),来识别目前模块的状态。

  • 依赖分析

    在模块加载后,我们需要解析出每个模块的绝对路径(path)、依赖模块(deps)和回调函数(callback),然后也放在模块信息中。模块对象管理逻辑的数据模型如下所示。

    {
    path: 'http://asdas/asda/a.js',
    deps: [{}, {}, {}],
    callback: function(){ },
    status: 'pending'
    }
  • 依赖循环

    模块很可能出现循环依赖的情况。也就是a模块和b模块相互依赖。依赖分为 强依赖弱依赖强依赖 是指,在模块回调执行时就会使用到的依赖;反之,就是 弱依赖 。对于 强依赖,会造成死锁,这种情况是无法解决的。但 弱依赖 可以通过现将一个空的模块引用注入让一个模块先执行,等依赖模块执行完后,再替换掉就可以了。 强依赖弱依赖 的例子如下:

//强依赖的例子
//A模块
define(['b.js'], function(B) { 
 // 回调执行时需要直接用到依赖模块
B.demo=1; // 其他逻辑});//B模块define(['a.js'], function(A) { // 回调执行时需要直接用到依赖模块
A.demo=1; // 其他逻辑});
// 弱依赖的例子
// A模块
define(['b.js'], function(B) { 
 // 回调执行时不会直接执行依赖模块
function test() {
B.demo=1;
} return {testFunc: test}
});//B模块define(['a.js'], function(A) { // 回调执行时不会直接执行依赖模块
function test() {
A.demo=1;
} return {testFunc: test}
});

3. 对外暴露define方法

对于define函数,需要遍历所有的未处理js脚本(包括 内联外联 ),然后执行模块的加载。这里对于 内联外联 脚本中的define,要做分别处理。主要原因有两点:

  1. 内敛脚本不需要加载操作。

  2. 内敛脚本中define的模块的回调输出是不能作为其他模块的依赖的。

var handledScriptList=[];
window.define=function(deps, callback) { var scripts=document.getElementsByTagName('script'),
defineReg=/s*define\s*\(\[.*\]\s*\,\s*function\s*\(.*\)\s*\{/,
script; for (var i=scripts.length - 1; i >=0; i--) {
script=list[i]; if (handledScriptList.indexOf(script.src) < 0) {
handledScriptList.push(script.src); if (script.innerHTML.search(defineReg) >=0) { // 内敛脚本直接进行模块依赖检查。
} else { // 外联脚本的首先要监听脚本加载
}
}
}
};

上面就是对实现一个模块化工具所涉及核心问题的描述。

者:肖鹏

转发链接:https://mp.weixin.qq.com/s/8gMg8pL1d89ofvc8FMiMBA

大家好,我是小雨!

今天我们要介绍的知识点是 Django 中的 Templates,它也是 MVT 结构中的 T,Templates 可以叫它模板。Templates 究竟用来做什么,通过这一节的介绍,希望大家有一个基本的了解。

一、Templates 模板

在上一节当中,我们在浏览器中看到的是 HTTPResponse 返回的字符串,而这一节我们讲的 templates 最直接的功能是把前端的 html 页面显示出来,这样用户就能看到清晰美观的界面。那么 Django 是如何加载 html 网页的呢?下面介绍两种方式:

1.使用 render_to_string 加载 html

我们提前准备好一个 html 页面index.html,然后为了方便 views 中能成功访问到这个index.html,需要在settings.py中将templates文件夹的路径声明清楚。在 TEMPLATES 列表字典的DIRS属性下添加一下内容:

os.path.join(BASE_DIR, 'templates')  # 如果缺少os模块记得导入

render_to_string 的使用方法: 先使用from django.templates.loader import render_to_string导入模块,然后给 render_to_string 传入 html 文件后,最后将其以 HttpResponse 的方式返回出去即可。

from django.http import HttpResponse
from django.templates.loader import render_to_string

def index(request):
    html = render_to_string("index.html")
    return HttpResponse(html)

2. 使用 render 加载 html

这里我们需要从django.shortcut导入 render 模块,一般情况下 Django 会默认导入这个模块,views 中如果没有的话就手动导入一下:

from django.shortcut import render

接着 render 中传入 html 文件,记得添加 request 参数,然后使用 return 返回 render 即可:

def index(request):
    return render(request,'index.html')

最后记得在 urls.py 中将路径对应的 views.index 添加进来就可以了。

二、模板语言的基本使用

我们要知道 templates 作为 MVT 架构中的 T,不仅仅是加载前端中的页面,它还包含一种模板语言,能够在 htmlL 语言中实现逻辑控制(条件选择、循环),相对于 JavaScript 会更加的灵活。

常用的python模板语言引擎

接下来,我们就以一个简单的例子来介绍 Django 模板语言的使用。

1.功能需求分析

我们有一个存储学生信息的Students.csv文件,包含了学号、姓名、性别、手机号码、出生日期、电子邮箱等信息。

319001,赵一,男,1998/12/27,18706012232,532211428@qq.com,北京市海淀区颐和园路5号,342622199801144314,2019/9/1,计算机,赵一,13655512212
319002,钱二,女,1995/10/25,13459732456,572501101@qq.com,北京市海淀区双清路30号,342622199709066819,2019/9/1,物联网,王二,13856909992

这里数据其实有很多条,为了减少篇幅我们就罗列出两条。

现在需要将这些数据加载到前端的 html 页面中,通过 tabel 标签显示出来。

2. 实现过程

(1)准备

首先用 pycharm 新建一个 Django 项目,为了方便使用直接把 application 和 templates 文件夹都建好。

pycharm新建Django项目

在 templates 中新建一个 index.html 文件,写好用于显示学生信息的标题以及表格框架。

<div id="title">学生信息列表</div>
<div id="table_main">
    <table border="1">
        <thead>
            <tr>
                <th>学号</th>
                <th>姓名</th>
                <th>性别</th>
                <th>出生日期</th>
                <th>手机号码</th>
                <th>电子邮箱</th>
                <th>家庭地址</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
</div>

(2)views 设置

为了读取 csv 文件,写一个read_form_file方法,将文件中的信息存储在 Students 列表中。

def read_from_file(path:str):
    students = []
    try:
        with open(path, mode="r", encoding="UTF-8") as fd:
            # 读取当前行
            current_line = fd.readline()
            # 判断当前行是否为空
            while current_line:
                temp_line = current_line.split(",")
                students.append(temp_line)
                current_line = fd.readline()
        return students
    except Exception as e:
        raise e

接下来,我们定义一个 student 方法,用来显示 html 内容给前端,这里面我们还是使用 render 将获取的数据传递给前端页面index.html

注意使用 context 参数,它的值我们设置为一个字典,键设置为 DTL 模板语言中要引用的变量,值设置为本地获取的数据

def student(request):
    students = read_from_file(r"D:\Python\Project\Demo\Student.csv")
    return render(request, "index.html", context={"all_student":students})

(3)DTL 模板语言

index.html中,表格内容的填充,我们通过 DTL 模板语言来完成。在 HTML 代码中要插入 DTL 模板语言,需要使用{%语句%},for 语句还要使用{% endfor %}来结尾,在语句内部调用变量列表中的元素也是直接使用变量名.数字并且需要包裹两层大括号。这里我们以循环输出 student 列表中的各个元素为例,其 DTL 模板语言的写法如下:

 {% for student in all_student %}
    <tr>
    <td>{{ student.0 }}</td>
    <td>{{ student.1 }}</td>
    <td>{{ student.2 }}</td>
    <td>{{ student.3 }}</td>
    <td>{{ student.4 }}</td>
    <td>{{ student.5 }}</td>
    <td>{{ student.6 }}</td>
    </tr>
 {% endfor %}

(4)urls 配置

接下来配置路由,添加 student 路径

path('student/',app01_views.student),

(5)运行效果

一切准备就绪后,执行python manage.py runserver,在浏览器中打开127.0.0.1:8000/student效果如下:

数据被成功加载

三、加载静态文件

1. 什么是静态文件

不能与服务器做动态交互的文件叫静态文件; 对于 HTML 中的图片、视频、css、js 这些都属于静态文件。Django 对于这些文件有一套明确的管理机制。 如果我们只遵循 HTML 的规范去加载静态文件,在 Django 中是显示不出来的。

Django无法加载静态文件

2. 如何加载静态文件

为了解决这个问题,我们首先要确保配置文件 settings.py 中 INSTALLED_APP 下静态文件模块有被加载到:

'django.contrib.staticfiles',  # 必须确保安装静态模块

然后使用STATICFILES_DIRS这个全局变量来存储静态文件的路径。

STATICFILES_DIRS = [
    os.path.join(BASE.DIR, 'static'),
]

然后在 html 中开始加上模板语言,如果让 html 引用静态文件,那就需要先导入静态模块。在 html 语句第一行写上

{% load static %}

对于要引用静态图片的标签路径使用{% static "路径文件名 "%},如:

<img src="{% static "logo.png"%}">

这样 Django 就能显示静态文件图片了。

3. 显示效果

静态图片被正确显示了


4. 注意事项

一般情况下,static 文件夹我们会放在app目录下。这时候访问静态文件,则需要在STATICFILES_DIRS的 os.path.join 参数中再多添加一个子文件夹名,可以写成:

STATICFILES_DIRS = [
    os.path.join(BASE.DIR, 'app01','static'),
]

5. 直接访问静态文件

在 Django 配置的文件 settings.py 中有一个全局变量STATIC_URL,它定义了一个给外界用户直接访问静态文件的路径。 上面我们添加在标题中的 logo.png,我们可以在浏览器中直接通过127.0.0.1:8000/student/static/logo.png来访问。

直接访问静态文件

四、加载静态文件综合案例

为了更好地巩固 Django 加载静态文件的知识,我们做一个小案例。学生信息管理系统的登录页面。有显示图片 logo,有用户名和密码的输入框,并且有提交按钮。当点击按钮时,会弹出提示表示登录成功。

这里简单说明一下:

界面采用了 Bootstrap 框架,登录按钮的功能函数用了 jQuery 脚本。关于前端的知识我们这里就不做过多介绍了。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <!--加载外部的CSS文件-->
    <link type="text/css" rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
    <link type="text/css" rel="stylesheet" href="{% static "css/basic.css" %}">
    <!--加载外部的js文件-->
    <script src="{% static "js/jquery.min.js" %}"></script>
    <script src="{% static "js/login.js" %}"></script>
</head>
<body>
    <div class="container">
        <div id = "login">
            <form class="form">
                <h2><img src="{% static "imags/mainlogo.png" %}"></h2>
                <h2>学生信息管理系统登录</h2>
                <hr>
                <hr>
                <div class="form-group">
                    <label for="user">用户名:</label>
                    <input type="text" class="form-control" placeholder="请输入用户名" id="user">
                </div>
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="password" class="form-control" placeholder="请输入密码" id="password">
                </div>
                <div class="form-group">
                    <div class="checkbox">
                        <label>
                            <input type="checkbox">请记住我
                        </label>
                    </div>
                </div>
                <button id="submit01" class="form-control">提交</button>
            </form>
        </div>
    </div>
</body>
</html>

将以上 html 文件保存在 templates 中,在 views 中配置好方法,urls 中配置好路由,最后在 settings 中配置好静态文件路径,最后启动 Django 服务器,页面显示效果如下:

效果演示:

登录页面案例演示


最后

本节,我们介绍了 Django 中 templates 的知识,Django 加载 HTML 方法、模板的语言的使用,以及加载静态文件的方法。希望大家多多练习,才能巩固所学的知识。下一节我们将继续介绍 Django 中 URL 跳转的知识!

感谢大家的阅读!