Node.js with Express:在Jade视图中使用脚本标签导入客户端JavaScript?

21

我有一个使用Jade模板引擎的node.js express服务器。

我有一个布局Jade文件,它会像这样导入各个视图的正文:

!!!
html

    head
        title= title || 'Title not set.'

    body
        #header
            h1 Header.

        #content!= body //- this renders the body of an individual view

        #footer
            p Footer.

例如,以下索引页面:

p Welcome to the front page.

p This page serves as a now.js test.

这个很好用。但是,我现在想要为这个索引页面特别包含两个客户端JavaScript库(因此不适用于每个页面,这就是为什么我不能将其放在布局的头部)。

这个有效:

//- import jquery
script(type='text/javascript', src='./jquery-1.5.2.min.js');

//- import now.js (hosts itself)
script(type='text/javascript', src='/nowjs/now.js')

//- import the chat client
script(type='text/javascript', src='./indexChatClient.js')

p Welcome to the front page.

p This page serves as a now.js test.

然而,这样会将脚本加载到完整页面的body中,这不是有效的HTML,对吗?

据我所知,如果要正确处理,应该将脚本加载到head中,但是head部分由布局文件处理。

那么,我应该如何为特定的视图/页面正确地包含这些客户端JavaScript库呢?

6个回答

40

您可以将库放置在布局上,并指定在“控制器”上加载哪些库。

// layout.jade
!!!
html

    head
        title= title || 'Title not set.'
        -each script in scripts 
          script(type='text/javascript', src= script)
    body
        #header
            h1 Header.

        #content!= body //- this renders the body of an individual view

        #footer
            p Footer.

你的“控制器”:

// app.js
app.get('/', function (req, res) {
  res.render({
    scripts: ['jquery.min.js', '/nowjs/now.js']
  }
}

1
我想这样做更好,因为脚本不应该在视图中设置,而应该在控制器中设置。 - Tom
2
我更喜欢将它们放在控制器上,这样你可以轻松地重用那些代码。 - masylum
3
如何设置一个可以在所有地方都包含的脚本,而不需要在单个渲染/视图中显式设置它? - Tom
5
为了使脚本在Express 3.0的每个视图中都出现,而无需在单个视图中显式设置它,您可以使用app.locals,例如:app.locals.globalScripts = ['/lib/jquery-min.js', '/lib/bootstrap.min.js'];然后在视图中可以这样写:-each script in globalScripts script(type='text/javascript', src= script)上述是按照masylum的回答所述。 - hillmark
但是这样是否仍会在所有视图中加载所有脚本?我认为OP想要仅在特定视图中加载,对吗? - CocoHot

20

我已经根据这个帖子使用了同样的解决方案:

http://groups.google.com/group/express-js/browse_thread/thread/8c2006dc7bab37b1/f9a273c836e0a2ac

你可以在视图选项中声明一个名为“scripts”的变量:

app.js:

app.set('view options', { locals: { scripts: ['jquery.js'] } });  // You can declare the scripts that you will need to render in EVERY page

你可以编写一个助手函数,将脚本标签渲染到布局的头部。

renderScriptTags() 助手函数代码:

app.helpers({ renderScriptTags: function(scripts) {
  return scripts.map(function(script) {
    return '<script src="scripts/' + script + '"></script>';
  }).join('\n ');

在头部区域的布局模板中,您将会有:

- renderScriptTags(scripts)

现在,如果您想在head标签中添加一个脚本,您只需要将该脚本推送到jade内容模板(body模板)的“scripts”变量中即可:

- scripts.push('myscript.js'); 

这种方式会将jquery.js和myscript.js渲染到页面头部。

更新

最新的express版本似乎以不同的方式处理本地变量,为了使其正常工作,您可以这样做(虽然我不确定这是否是最佳解决方案,我需要再深入了解一下)

您可以像以前一样在布局模板中使用renderScriptTags()助手。

但不要将脚本变量设置为本地变量,而是创建一个动态助手,在我们的模板中创建一个scripts变量。

app.dynamicHelpers({
  scripts: function(req, res){
    return ['jquery.js']; //this will be available in all views
  }
});

然后,要添加特定的脚本,从您的 body 模板中(与之前完全相同):

- scripts.push('myscript.js'); 

现在,对于这个特定的视图,您应该正确呈现jquery.js和myscript.js


@Tom:我注意到在较新版本的Express中,本地变量的处理方式有所不同。我在我的更新答案中提供了一个修改后的解决方案(也许有更好的解决方案,但这应该可以工作)。 - BFil
@Tom:是的,在你的内容模板中。 - BFil
@ShadowCloud,脚本是一个函数,而不是数组,因此您不能使用push等方法。这样是行不通的。 - Tom
@ShadowCloud 我正在尝试在我的布局中使用你的renderScriptsTags函数。我通过引用“- renderScriptTags(scripts)”在头部调用它。我已经在调试器中检查过了,你的函数正确生成了脚本标签,但它们没有被放置到jade生成的html中。自从你回答这个问题以来,有什么改变了helpers和jade的工作方式吗?谢谢! - Evan
我不确定,对我来说似乎没有变化,但你可以自行检查更改日志: https://github.com/visionmedia/express/blob/master/History.md 和 https://github.com/visionmedia/jade/blob/master/History.md - BFil
显示剩余7条评论

8

在最新的Jade(0.28.1)中,可以通过将所有内容保留在模板/视图中而不是在其他地方(脚本链接)中篡改页面内容来以正确的方式完成它:

  • 将头声明为模板中的命名块:
doctype 5
html
  head
    // 命名块允许我们在每个页面中添加自定义头条目
    block head
        title= title
        link( rel='stylesheet', href='/css/style.css' )
        script( type="text/javascript", src="/js/some-default-script.js" )
  body
    block content
  • 在视图中附加特定于页面的头元素(包括脚本标记):
extends layout
// 这里我们引用了模板头并将其附加到其中 block append head meta( name="something", content="blah" ) link( href="/css/another.css", rel="stylesheet", type="text/css" ) style div.foo { position: absolute; } script( src="/js/page-specific-script.js" )
block content #page-contents-follows

2
这里提供了另一种方法来实现(使用ShadowCloud的答案)。稍微通用化一点,你可以指定本地和远程脚本,然后在页面加载后再延迟它们的执行:
app.js:
app.dynamicHelpers({
    scripts: function() {
        //scripts to load on every page
        return ['js/jquery.min.js','js/jquery-ui.min.js','js/all.js'];
    }
});

然后,您可以在视图内的任何位置添加本地或远程脚本。
//- local script
- scripts.push('js/myPage.js');
//- remote script ( note: this is a schemeless url. You can use http(s)? ones too )
- scripts.push('//platform.twitter.com/widgets.js')

layout.jade: (我把它放在body的末尾,以先加载可见内容,但实际上可以放在任何位置)

//- Bring the scripts into a client-side array,
//-    and attach them to the DOM one by one on page load
script
    var page_scripts = !{'["' + scripts.join('","') + '"]'};
    function loadJS() {
        for(var i in page_scripts) {
            var e = document.createElement("script");
            e.src = page_scripts[i];
            document.body.appendChild(e);
        }
    }
    // Check for browser support of event handling capability
    if (window.addEventListener)
        window.addEventListener("load", loadJS, false);
    else if (window.attachEvent)
        window.attachEvent("onload", loadJS);
    else window.onload = loadJS;

2

我猜测(简单浏览后)的问题在于您没有刷新数组,即将其.length设置为0以删除旧值,因此每个请求都可能会推送更多的字符串。


但我不需要这样做,对吧?我认为本地内容在每次视图呈现时都会被设置。另外,这种添加脚本的方式是否非常低效,因为每个请求基本上都有相同的脚本,因此应将其缓存为固定的HTML,而不是每次迭代数组? - Tom

1

我不确定目前为止的方法的意义所在。对我来说,以下做法更加简洁...

layout.jade:

doctype html
html
  head
    title= title
    block headscripts  // placeholder for scripts that need to be in the <head>
    link(rel='stylesheet', href='/styles/style.css')
    block styles       // placeholder for styles
  body
    block content
    script(src='/libs/jquery/dist/jquery.min.js') // this will render before all scripts in every page
    block scripts  // placeholder for scripts that go in the body

somepage.jade:

extends layout

block styles  // this will render in the <head>
  link(rel='stylesheet', href='/styles/films.css')
  link(rel='stylesheet', href='/styles/pagination.css')

block headscripts  // this will also render in the <head>
  script(src='/js/somescript.js')

block content
  h1= title
  div.someotherstuff

block scripts  // this will render at the end of the body
  script(src='/js/libs/someotherscript.js')
  scirpt(src='/libs/doT/doT.js')

这样一来,您无论把块放在.jade页面的哪个位置,它们都会始终在正确的位置呈现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接