一个页面请求的生命周期

说明

非常有价值的一节课,让你了解fastadmin的一个页面请求,背后都经历了哪些动作,将框架前端后端的关键代码文件给串接起来。以前你可以按开发规范编写代码片段,但可能不知道框架和这个代码片段有什么关系,学完你知道为什么这些代码片段是如何和框架代码配合起来来完成一个事情的。

后端部分处理的内容

1、浏览器请求一个路径

例如 https://www.feige.team/index/user/index.html

2、nginx服务器重定向到index.php

将重定向到 /index.php?s=index/user/index.html

location / {
    if (!-e $request_filename) {
        rewrite  ^(.*)$  /index.php?s=$1  last;   break;
    }
}

3、调用index.php页面

页面位置:/public/index.php,里面触发加载thinkphp框架

// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';

4、触发启动thinkphp框架启动

并做一系列动作,参考:https://www.kancloud.cn/manual/thinkphp5/118011
其中最为重要的就是解析url,路由到相应的模块的controll-action里去。
例如上面index/user/index.html,就会路由到index模块下是User控制器里的index方法
输入图片说明

5、控制器的对应的方法返回html

例如上面的例子,就是User控制器里的index方法来返回html的内容,通过如下的方法:

return $this->view->fetch();

在view里取同名的index.html文件的内容。为什么是取了和方法同名的html文件,这是thinkphp框架的底层逻辑,先不钻进去研究了。可以阅读:https://www.kancloud.cn/manual/thinkphp5/118114

6、layout 布局模板组装view的html

上面的view的html里是没有header这些内容的,说明在其他地方来组装。
取到这个view的内容要后要塞到模板文件里,在这个控制器里$layout 变量定义使用哪个布局模板文件

class User extends Frontend
{
    protected $layout = 'default';
    ...
}

表示模板文件的路径为:view/layout/文件夹下的default.html
view的内容到时就会替换布局模板里的 {__CONTENT__}字符。
通过layout 布局模板组装,这也是thinkphp框架的实现的,也不钻进去研究了。可以阅读:https://www.kancloud.cn/manual/thinkphp5/125013

7、解析模板里的变量

变量的值在对应的方法里使用$this->view->assign来定义

    public function index()
    {
        $this->view->assign('title', __('User center'));
        return $this->view->fetch();
    }

8、返回解析后的html内容给浏览器

前端部分处理的内容

9、浏览器再次向服务器请求js/css/img等静态资源文件

浏览器解析html的内容,请求js/css/img。其中js的请求过程很复杂,因为使用了requirejs来实现按需加载js文件。

9.1、view/layout/default.html

请求静态资源的代码是在模板文件里。其中关键是调用了两个代码块:

 {include file="common/meta" /}
 {include file="common/script" /}

common//meta.html文件里关键内容如下:

<script type="text/javascript">
    var require = {
        config: {$config|json_encode}
    };
</script>

这个信息会被合并到requirejs.config的config里面:

requirejs.config({
    ...
    config: {
        site: {
            version: '1.0.0'
        }
    }
});

$config表示将从后台获取的当前请求的模块、控制名、方法名等。在这个是后台来定义了config的值:
输入图片说明

common/script.html文件关键内容如下:

<script src="__CDN__/assets/js/require{$Think.config.app_debug?'':'.min'}.js" data-main="__CDN__/assets/js/require-frontend{$Think.config.app_debug?'':'.min'}.js?v={$site.version|htmlentities}"></script>

表示将这个文件调用了requirejs,并且主js文件为/assets/js/require-frontend.js

9.2、require-frontend.js

这个文件定义了requirejs的重要的配置信息。这个后续单点展开讲。
另外最重要的是在这个文件里根据common/meta.html文件提到的config,获取到此页面对应的模块、控制名、方法名。
然后requirejs去加载此页面对应的js文件。

require(['jquery', 'bootstrap'], function ($, undefined) {
    ...
    var Config = requirejs.s.contexts._.config.config;
    ...

    // 初始化
    $(function () {
        require(['fast'], function (Fast) {
            require(['frontend', 'frontend-init', 'addons'], function (Frontend, Addons) {
                //加载相应模块                
                if (Config.jsname) {
                    require([Config.jsname], function (Controller) {
                        Controller[Config.actionname] != undefined && Controller[Config.actionname]();
                    }, function (e) {
                        console.error(e);
                        // 这里可捕获模块加载的错误
                    });
                }
            });
        });
    });
});

上面关键的Config = requirejs.s.contexts._.config.config 获取到众多信息:当前请求的模块、控制名、方法名。
其中Config.jsname 获取的就是对应的js路径,为:frontend/user.js
然后通过Controller[Config.actionname]()再调用这个js文件里的对应定义方法(如果存在就调用)。例如在上面的例子里,Controller[Config.actionname]()就是会调用这个函数Controller["index"]()。
这样就实现了: 根据index/user/index.html,加载了对应的js文件frontend/user.js,并调用了js里面的Controller类里的index函数。
如果请求的是index/user/login.html,并 调用了js里面的Controller类里的login函数。
输入图片说明

--B站/抖音:写代码的产品飞哥

--分享运营真实案例,用编程创造自己的产品