จะบังคับให้เบราว์เซอร์โหลดไฟล์ CSS / JS ที่แคชได้อย่างไร


993

ฉันสังเกตเห็นว่าบางเบราว์เซอร์ (โดยเฉพาะ Firefox และ Opera) มีความกระตือรือร้นอย่างมากในการใช้สำเนาไฟล์. cssและ. js ที่แคชไว้แม้ระหว่างช่วงเบราว์เซอร์ สิ่งนี้นำไปสู่ปัญหาเมื่อคุณอัปเดตหนึ่งในไฟล์เหล่านี้ แต่เบราว์เซอร์ของผู้ใช้ยังคงใช้แคชที่คัดลอก

คำถามคือ: วิธีที่ดีที่สุดในการบังคับให้เบราว์เซอร์ของผู้ใช้โหลดไฟล์ใหม่เมื่อมีการเปลี่ยนแปลงคืออะไร?

เป็นการดีที่โซลูชันจะไม่บังคับให้เบราว์เซอร์โหลดไฟล์ซ้ำทุกครั้งที่เข้าชมหน้าเว็บ ฉันจะโพสต์คำตอบของตัวเอง แต่ฉันอยากรู้ว่าใครมีวิธีแก้ปัญหาที่ดีกว่าและฉันจะให้คะแนนโหวตของคุณตัดสินใจ

อัปเดต:

หลังจากให้การสนทนาที่นี่ซักพักฉันพบว่าคำแนะนำของJohn Millikinและda5idมีประโยชน์ ปรากฎว่ามีคำสำหรับสิ่งนี้: การกำหนดเวอร์ชันอัตโนมัติ

ฉันโพสต์คำตอบใหม่ด้านล่างซึ่งเป็นการผสมผสานระหว่างโซลูชันดั้งเดิมและคำแนะนำของจอห์น

แนวคิดอื่นที่แนะนำโดยSCdFก็คือการผนวกสตริงข้อความค้นหาปลอมเข้ากับไฟล์ (รหัสไพ ธ อนบางตัวจะใช้การประทับเวลาโดยอัตโนมัติเนื่องจากสตริงการสืบค้นที่ปลอมเป็นการส่งโดยpi .) อย่างไรก็ตามมีการสนทนากันว่าเบราว์เซอร์จะแคชไฟล์ด้วยสตริงการสืบค้นหรือไม่ (โปรดจำไว้ว่าเราต้องการให้เบราว์เซอร์แคชไฟล์และใช้ในการเข้าชมในอนาคตเราต้องการให้เรียกไฟล์อีกครั้งเมื่อมีการเปลี่ยนแปลง)

เนื่องจากยังไม่ชัดเจนว่าเกิดอะไรขึ้นกับสตริงข้อความปลอมฉันจึงไม่ยอมรับคำตอบนั้น


ฉันมีนี้ใน htaccess ของฉันและไม่เคยปัญหาใด ๆ ExpiresActive On ExpiresDefault "modification"กับแคชไฟล์:
Frank Conijn

2
ฉันเห็นด้วยอย่างแน่นอนว่าการเพิ่มข้อมูลการกำหนดเวอร์ชันไปยัง URL ของไฟล์นั้นเป็นวิธีที่ดีที่สุด มันทำงานได้ตลอดเวลาสำหรับทุกคน แต่ถ้าคุณไม่ได้ใช้งานและคุณต้องโหลดไฟล์ CSS หรือ JS ใหม่เป็นครั้งคราวในเบราว์เซอร์ของคุณเอง ... เพียงแค่เปิดในแท็บของตนเองแล้วกด SHIFT-reload (หรือ CTRL-F5)! คุณสามารถทำได้อย่างมีประสิทธิภาพในสิ่งเดียวกันโดยใช้ JS โดยการโหลดไฟล์ใน (ซ่อน) iframe iframe.contentWindow.location.reload(true)รอจนกว่าจะโหลดแล้วโทร ดูวิธีการ (4) ของstackoverflow.com/a/22429796/999120 - เกี่ยวกับรูปภาพ แต่ใช้วิธีเดียวกัน
Doin

2
ฉันซาบซึ้งในวิธีการถามคำถามนี้และได้รับการปรับปรุงตั้งแต่นั้นมา มันอธิบายสิ่งที่ฉันควรคาดหวังในคำตอบอย่างสมบูรณ์ ฉันจะทำตามวิธีการนี้ในคำถามของฉันนับจากนี้ ไชโย!
rd22

คำตอบ:


455

ปรับปรุง: เขียนใหม่เพื่อรวมข้อเสนอแนะจากจอห์น Millikinและda5id โซลูชันนี้เขียนด้วย PHP แต่ควรปรับให้เข้ากับภาษาอื่นได้ง่าย

การปรับปรุงที่ 2:การผสมผสานความคิดเห็นจากนิคจอห์นสันที่เดิม.htaccessregex json-1.3.jsสามารถทำให้เกิดปัญหากับไฟล์เช่น วิธีแก้ไขคือการเขียนเฉพาะเมื่อมีตัวเลข 10 หลักตรงที่สุด (เนื่องจาก 10 หลักครอบคลุมการประทับเวลาทั้งหมดตั้งแต่ 9/9/2001 ถึง 11/20/2286)

อันดับแรกเราใช้กฎการเขียนซ้ำต่อไปนี้ใน. htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

ตอนนี้เราเขียนฟังก์ชัน PHP ต่อไปนี้:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

ตอนนี้ที่ใดก็ตามที่คุณรวม CSS ของคุณให้เปลี่ยนจากนี้:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

สำหรับสิ่งนี้:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

ด้วยวิธีนี้คุณไม่ต้องแก้ไขแท็กลิงก์อีกครั้งและผู้ใช้จะเห็น CSS ล่าสุดเสมอ เบราว์เซอร์จะสามารถแคชไฟล์ CSS ได้ แต่เมื่อคุณทำการเปลี่ยนแปลงกับ CSS เบราว์เซอร์จะเห็นสิ่งนี้เป็น URL ใหม่ดังนั้นจะไม่ใช้สำเนาที่แคช

สิ่งนี้สามารถทำงานกับรูปภาพ favicons และ JavaScript โดยทั่วไปสิ่งใดก็ตามที่ไม่ได้สร้างแบบไดนามิก


16
เซิร์ฟเวอร์เนื้อหาแบบคงที่ของฉันเองทำสิ่งเดียวกันทุกประการยกเว้นฉันใช้พารามิเตอร์สำหรับการกำหนดเวอร์ชัน (base.css? v = 1221534296) แทนที่จะเปลี่ยนชื่อไฟล์ (base.1221534296.css) ฉันสงสัยว่าวิธีการของคุณอาจจะมีประสิทธิภาพมากกว่านิดหน่อย เด็ดมาก
Jens Roland

4
@Kip: โซลูชันเนียนมาก เห็นได้ชัดว่าการเขียน URL ใหม่นั้นมีอะไรมากกว่าสิ่งที่คุณมอบ
James P.

37
ฉันเห็นปัญหาเกี่ยวกับสิ่งนี้มันสามารถเข้าถึงระบบไฟล์ได้หลายครั้ง - จำนวนลิงก์ - จำนวนการร้องขอ / วินาที ... ที่อาจหรือไม่อาจเป็นปัญหาสำหรับคุณ
Tomáš Fejfar

3
@AlixAxel: ไม่เบราว์เซอร์จะดึงข้อมูลอีกครั้งเมื่อพารามิเตอร์เปลี่ยนแปลง แต่ผู้รับมอบฉันทะสาธารณะบางรายจะไม่แคชไฟล์ด้วยพารามิเตอร์ url ดังนั้นวิธีปฏิบัติที่ดีที่สุดคือการรวมเวอร์ชันไว้ในพา ธ และค่าใช้จ่าย mod_rewrite นั้นเล็กเมื่อเทียบกับคอขวดของประสิทธิภาพการทำงานอื่น ๆ ใน WPO
Jens Roland

8
การfile_existsตรวจสอบครั้งแรกจำเป็นหรือไม่? filemtimeจะคืนค่า false เมื่อเกิดความล้มเหลวดังนั้นทำไมไม่เพียงกำหนดค่า filemtime ให้กับตัวแปรและตรวจสอบว่าเป็นเท็จก่อนเปลี่ยนชื่อไฟล์หรือไม่ ที่จะลดการดำเนินการไฟล์ที่ไม่จำเป็นครั้งเดียวซึ่งจะเพิ่มขึ้นจริง ๆ
Gavin

184

เทคนิคฝั่งไคลเอ็นต์อย่างง่าย

โดยทั่วไปแล้วการแคชเป็นสิ่งที่ดี .. ดังนั้นจึงมีสองสามเทคนิคขึ้นอยู่กับว่าคุณกำลังแก้ไขปัญหาด้วยตัวคุณเองเมื่อคุณพัฒนาเว็บไซต์หรือไม่หรือคุณพยายามควบคุมแคชในสภาพแวดล้อมการผลิต

ผู้เข้าชมทั่วไปในเว็บไซต์ของคุณจะไม่มีประสบการณ์แบบเดียวกับที่คุณมีเมื่อคุณพัฒนาเว็บไซต์ เนื่องจากผู้เข้าชมโดยเฉลี่ยมาที่ไซต์น้อยกว่านี้ (อาจมีเพียงไม่กี่ครั้งต่อเดือนยกเว้นว่าคุณเป็น Google หรือเครือข่าย hi5) พวกเขามีโอกาสน้อยที่จะมีไฟล์ของคุณในแคชและอาจเพียงพอ หากคุณต้องการบังคับให้มีเวอร์ชันใหม่ลงในเบราว์เซอร์คุณสามารถเพิ่มสตริงการสืบค้นลงในคำขอและชนหมายเลขรุ่นเมื่อคุณทำการเปลี่ยนแปลงที่สำคัญ:

<script src="/myJavascript.js?version=4"></script>

สิ่งนี้จะช่วยให้มั่นใจว่าทุกคนจะได้รับไฟล์ใหม่ ใช้งานได้เนื่องจากเบราว์เซอร์ตรวจสอบ URL ของไฟล์เพื่อตรวจสอบว่ามีสำเนาอยู่ในแคชหรือไม่ หากเซิร์ฟเวอร์ของคุณไม่ได้ตั้งค่าให้ทำอะไรกับสตริงการสืบค้นจะถูกละเว้น แต่ชื่อจะมีลักษณะเหมือนไฟล์ใหม่ในเบราว์เซอร์

ในทางกลับกันหากคุณกำลังพัฒนาเว็บไซต์คุณไม่ต้องการเปลี่ยนหมายเลขรุ่นทุกครั้งที่คุณบันทึกการเปลี่ยนแปลงเป็นเวอร์ชันการพัฒนาของคุณ นั่นจะน่าเบื่อ

ดังนั้นในขณะที่คุณกำลังพัฒนาเว็บไซต์ของคุณเคล็ดลับที่ดีคือการสร้างพารามิเตอร์สตริงการสืบค้นโดยอัตโนมัติ:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

การเพิ่มสตริงการสืบค้นลงในคำขอเป็นวิธีที่ดีในการกำหนดรุ่นทรัพยากร แต่สำหรับเว็บไซต์แบบง่ายอาจไม่จำเป็น และจำไว้ว่าการแคชเป็นสิ่งที่ดี

นอกจากนี้ยังเป็นที่น่าสังเกตว่าเบราว์เซอร์ไม่จำเป็นต้องตระหนี่เกี่ยวกับการเก็บไฟล์ไว้ในแคช เบราว์เซอร์มีนโยบายสำหรับสิ่งเหล่านี้และพวกเขามักจะเล่นตามกฎที่วางไว้ในข้อมูลจำเพาะ HTTP เมื่อเบราว์เซอร์ทำการร้องขอไปยังเซิร์ฟเวอร์ส่วนหนึ่งของการตอบกลับคือส่วนหัว EXPIRES .. วันที่ซึ่งบอกเบราว์เซอร์ว่าควรเก็บไว้นานเท่าใดในแคช ในครั้งต่อไปที่เบราว์เซอร์พบคำขอไฟล์เดียวกันจะเห็นว่ามีสำเนาในแคชและดูวันหมดอายุเพื่อตัดสินใจว่าควรใช้หรือไม่

เชื่อหรือไม่ว่าจริง ๆ แล้วมันเป็นเซิร์ฟเวอร์ของคุณที่สร้างแคชเบราว์เซอร์นั้นแบบถาวร คุณสามารถปรับการตั้งค่าเซิร์ฟเวอร์ของคุณและเปลี่ยนส่วนหัว EXPIRES ได้ แต่เทคนิคเล็กน้อยที่ฉันเขียนไว้ด้านบนอาจเป็นวิธีที่ง่ายกว่ามากสำหรับคุณ เนื่องจากการแคชเป็นสิ่งที่ดีคุณมักต้องการตั้งค่าวันที่นั้นให้ไกลออกไปในอนาคต ("ส่วนหัวในอนาคตอันใกล้หมดอายุ") และใช้เทคนิคที่อธิบายไว้ข้างต้นเพื่อบังคับให้มีการเปลี่ยนแปลง

หากคุณสนใจข้อมูลเพิ่มเติมเกี่ยวกับ HTTP หรือการร้องขอเหล่านี้หนังสือที่ดีคือ "เว็บไซต์ประสิทธิภาพสูง" โดย Steve Souders เป็นการแนะนำที่ดีมากสำหรับหัวเรื่อง


3
เคล็ดลับง่ายๆในการสร้างสตริงการสืบค้นด้วย Javascript นั้นยอดเยี่ยมในระหว่างการพัฒนา ฉันทำสิ่งเดียวกันกับ PHP
อลันทัวริง

2
นี่เป็นวิธีที่ง่ายที่สุดในการบรรลุผลลัพธ์ที่ต้องการของโปสเตอร์ต้นฉบับ วิธี mod_rewrite ทำงานได้ดีถ้าคุณต้องการบังคับให้โหลดไฟล์. css หรือ. js ทุกครั้งที่คุณโหลดหน้า วิธีนี้ยังคงอนุญาตให้แคชจนกว่าคุณจะเปลี่ยนแปลงไฟล์และต้องการให้โหลดซ้ำ
scott80109

@ keparo ฉันมีจำนวน jquery เพียงพอในทุกหน้าถ้าฉันจะเปลี่ยนมันด้วยตนเองมันจะใช้เวลาหนึ่งเดือน หากคุณสามารถช่วยฉันแก้ปัญหาได้โดยไม่ต้องเข้ารหัสแต่ละหน้า
แครกเกอร์

1
ดูเหมือนจะใช้งานไม่ได้กับ CSS ของฉันเมื่อฉันใช้:<link href='myCss.css?dev=14141'...>
Noumenon

3
นี่ไม่ใช่วิธีแก้ปัญหาที่ทำงานได้ เบราว์เซอร์จำนวนมากจะปฏิเสธที่จะแคชสิ่งใด ๆ ด้วยสตริงการสืบค้น นี่คือเหตุผลที่ Google, GTMetrix และเครื่องมือที่คล้ายกันจะยกธงหากคุณมีสตริงแบบสอบถามในการอ้างอิงถึงเนื้อหาคงที่ แม้ว่ามันจะเป็นทางออกที่ดีสำหรับการพัฒนา แต่ก็ไม่ใช่ทางออกสำหรับการผลิต นอกจากนี้เบราว์เซอร์จะควบคุมการแคชไม่ใช่เซิร์ฟเวอร์ เซิร์ฟเวอร์เพียงแค่แนะนำเมื่อควรรีเฟรช; เบราว์เซอร์ไม่จำเป็นต้องฟังเซิร์ฟเวอร์ (และไม่บ่อย) อุปกรณ์มือถือเป็นตัวอย่างสำคัญของสิ่งนี้
เนทฉัน

113

ปลั๊กอินmod_pagespeedของ Google สำหรับ apache จะทำการกำหนดเวอร์ชันอัตโนมัติสำหรับคุณ มันลื่นจริงๆ

มันแยกวิเคราะห์ HTML ออกห่างจากเว็บเซิร์ฟเวอร์ (ทำงานกับ PHP, ทางรถไฟ, หลาม, HTML แบบคงที่ - อะไรก็ได้) และเขียนลิงค์ไปยัง CSS, JS, ไฟล์รูปภาพเพื่อให้พวกเขารวมรหัสประจำตัว มันให้บริการไฟล์ที่ URL ที่แก้ไขด้วยการควบคุมแคชนานมาก เมื่อไฟล์มีการเปลี่ยนแปลงมันจะเปลี่ยน URL โดยอัตโนมัติดังนั้นเบราว์เซอร์จะต้องดึงไฟล์เหล่านั้นอีกครั้ง มันใช้งานได้โดยไม่มีการเปลี่ยนแปลงโค้ดของคุณ มันจะทำให้โค้ดของคุณย่อเล็กลงด้วย


1
เยี่ยมมาก แต่ยังอยู่ในช่วงเบต้า สามารถใช้บริการองค์กรได้หรือไม่
Sanghyun Lee

26
นี่คือผิด (เล่นซอโดยอัตโนมัติกับแหล่งที่มา) เมื่อเห็นได้ชัดว่าเป็นปัญหาเบราว์เซอร์ ให้พวกเรา (นักพัฒนาซอฟต์แวร์) ฝึกสมองให้สดชื่น: <ctrl> + F5
T4NK3R

25
mod_pagespeed นั้นเทียบเท่ากับขั้นตอนการสร้าง / คอมไพล์โดยอัตโนมัติสำหรับ html / css / js ของคุณ ฉันคิดว่าคุณยากที่จะหานักพัฒนาที่จริงจังคนใดที่คิดว่าระบบการสร้างนั้นผิดปกติหรือไม่ว่ามันจะเป็นไปโดยอัตโนมัติ การเปรียบเทียบโครงสร้างแบบคลีนคือการล้างแคชของ mod_pagespeed: code.google.com/p/modpagespeed/wiki/… ?
Leopd

3
@ T4NK3R mod_pagespeed ไม่ต้องทำอะไรกับแหล่งที่มาของคุณเพื่อทำการจัดการแคชมันก็บอกว่ามันสามารถช่วยสิ่งต่าง ๆ เช่น minification ไม่ว่าจะเป็น "ผิด" หรือไม่ก็ตาม มันอาจจะไม่ถูกต้องสำหรับคุณ แต่ไม่ได้หมายความว่ามันเป็น instirinsically ที่ไม่ดี
Madbreaks

2
มันทำงานร่วมกับ nginx ด้วยแม้ว่าคุณจะต้องสร้างมันจากแหล่งที่มา: developers.google.com/speed/pagespeed/module/ …
Rohit

93

แทนที่จะเปลี่ยนรุ่นด้วยตนเองฉันขอแนะนำให้คุณใช้ MD5 แฮชของไฟล์ CSS จริง

ดังนั้น URL ของคุณจะเป็นอย่างไร

http://mysite.com/css/[md5_hash_here]/style.css

คุณยังคงสามารถใช้กฎการเขียนซ้ำเพื่อตัดแฮชออกได้ แต่ข้อดีคือตอนนี้คุณสามารถตั้งค่านโยบายแคชเป็น "แคชถาวร" ได้เนื่องจากหาก URL เหมือนกันนั่นหมายความว่าไฟล์จะไม่เปลี่ยนแปลง

จากนั้นคุณสามารถเขียนเชลล์สคริปต์อย่างง่ายที่จะคำนวณแฮชของไฟล์และอัปเดตแท็กของคุณ (คุณอาจต้องการย้ายไปไว้ที่ไฟล์แยกต่างหากเพื่อรวมไว้)

เพียงเรียกใช้สคริปต์นั้นทุกครั้งที่มีการเปลี่ยนแปลง CSS และคุณก็ทำได้ดี เบราว์เซอร์จะโหลดไฟล์ของคุณอีกครั้งเท่านั้นเมื่อมีการเปลี่ยนแปลง หากคุณทำการแก้ไขแล้วยกเลิกมันจะไม่มีความเจ็บปวดในการหาเวอร์ชั่นที่คุณต้องกลับไปเพื่อไม่ให้ผู้เยี่ยมชมดาวน์โหลดซ้ำ


1
น่าเสียดายที่ฉันไม่รู้วิธีใช้งาน คำแนะนำโปรด ... รายละเอียดเพิ่มเติม ...
Michael Phelps

การใช้งานในเปลือกทับทิม ฯลฯ จะดีมาก
ปีเตอร์

3
ทางออกที่ดีมาก .. แต่ฉันคิดว่ามันเป็นทรัพยากรที่ใช้ในการคำนวณการแฮชของไฟล์ในทุกคำขอไฟล์ (css, js, images, html .. ฯลฯ ) สำหรับการเข้าชมหน้าเว็บทุกหน้า
DeepBlue

นี้เป็นทางออกที่เป็นมาตรฐานสำหรับผู้ที่ใช้ js หรือ CSS bundling กับอึก, แสมหรือ webpack ที่แตกต่างกันการดำเนินงานสำหรับแต่ละวิธีการแก้ปัญหา แต่ hashing ไฟล์ของคุณเป็นขั้นตอนสร้างเป็นปกติและแนะนำสำหรับการปพลิเคชันที่ทันสมัยรวม
แบรนดอนSøren Culley

@DeepBlue - คำตอบว่า"ทำงานสคริปต์ที่เปลี่ยนแปลงทุก CSS เวลา" ไม่ได้อยู่ที่การเข้าชมทุกหน้า OTOH คำตอบจะให้รายละเอียดที่สำคัญแฮชที่มีการเปลี่ยนแปลงกลายเป็นส่วนหนึ่งของ URL อย่างไร ฉันไม่รู้ ...
ToolmakerSteve

70

ไม่แน่ใจว่าทำไมพวกคุณเจ็บปวดกับการนำโซลูชั่นนี้ไปใช้

สิ่งที่คุณต้องทำก็ต่อเมื่อได้รับการประทับเวลาที่แก้ไขแล้วของไฟล์และต่อท้ายด้วยการสอบถามไฟล์

ใน PHP ฉันจะทำตาม:

<link href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">

filemtime เป็นฟังก์ชั่น PHP ที่ส่งคืนไฟล์เวลาที่แก้ไข


mycss.css?1234567890คุณก็สามารถใช้
Gavin

3
สง่างามมากแม้ว่าฉันจะปรับเปลี่ยนเล็กน้อย<link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>แต่ในกรณีที่มีข้อโต้แย้งบางอย่างเกี่ยวกับหัวข้อนี้เกี่ยวกับการแคช URL ด้วยตัวแปร GET (ในรูปแบบที่แนะนำ) ถูกต้อง
luke_mclachlan

ต่อจากความคิดเห็นสุดท้ายของฉันฉันเห็นว่า wordpress ใช้?ver=ดังนั้นใครจะรู้!
luke_mclachlan

ทางออกที่ดี นอกจากนี้สำหรับฉันฉันพบว่า filemtime ไม่ทำงานสำหรับชื่อโดเมนแบบเต็ม (FQDN) ดังนั้นฉันจึงใช้ FQDN สำหรับส่วน href และ $ _SERVER ["DOCUMENT_ROOT"] สำหรับส่วน filemtime ตัวอย่าง: <link rel = "stylesheet" href = "http: //theurl/mycss.css? v = <? php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/mycss.css')?" />
rrtx2000

ขอบคุณมาก เรียบง่ายและดี นี่คือใน Python: progpath = os.path.dirname (sys.argv [0]) กำหนดเวอร์ชัน (ไฟล์): timestamp = os.path.getmtime ('% s /../ เว็บ /% s'% (progpath ไฟล์)) ส่งคืน '% s? v =% s'% (ไฟล์เวลาประทับ) พิมพ์ <link href = "% s" rel = "สไตล์ชีท" "'type =" text / css "/>' \% versionize ( 'css / main.css')
dlink

52

คุณสามารถใส่?foo=1234ส่วนท้ายของการนำเข้า css / js ของคุณเปลี่ยน 1234 เป็นสิ่งที่คุณต้องการ ลองดูที่ซอร์ส html SO เพื่อเป็นตัวอย่าง

ความคิดที่มีอยู่ว่า? พารามิเตอร์จะถูกยกเลิก / เพิกเฉยต่อคำร้องขอต่อไปและคุณสามารถเปลี่ยนหมายเลขนั้นได้เมื่อคุณเปิดตัวเวอร์ชันใหม่


หมายเหตุ:มีข้อโต้แย้งบางประการเกี่ยวกับวิธีที่สิ่งนี้มีผลต่อการแคช ฉันเชื่อว่าแก่นสารทั่วไปของมันคือการร้องขอ GET โดยมีหรือไม่มีพารามิเตอร์ควรจะสามารถเข้าถึงได้ดังนั้นวิธีการแก้ปัญหาข้างต้นควรทำงาน

อย่างไรก็ตามมันลงไปที่เว็บเซิร์ฟเวอร์ทั้งคู่เพื่อตัดสินใจว่ามันต้องการที่จะเป็นไปตามส่วนหนึ่งของข้อมูลจำเพาะและเบราว์เซอร์ที่ผู้ใช้ใช้เพราะมันสามารถไปข้างหน้าและขอรุ่นใหม่ได้


เรื่องไร้สาระ ข้อความค้นหา (พารามิเตอร์ aka. GET) เป็นส่วนหนึ่งของ URL พวกเขาสามารถและจะถูกแคช นี่เป็นทางออกที่ดี
troelskn

9
@troelskn: ข้อมูลจำเพาะ HTTP 1.1 ระบุไว้เป็นอย่างอื่น (สำหรับ GET และคำขอ HEAD ที่มีการสอบถามพารามิเตอร์): แคชจะต้องไม่ตอบสนองต่อ URIs เช่นนั้นใหม่เว้นแต่เซิร์ฟเวอร์จะให้เวลาหมดอายุอย่างชัดเจน ดูw3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Michael Johnson

4
ฉันลองใช้สตริงข้อความค้นหาของการกำหนดเวอร์ชันกับเบราว์เซอร์หลัก ๆ ทั้งหมดและพวกเขาทำการแคชไฟล์รายละเอียดหรือไม่ อย่างไรก็ตามฉันคิดว่าการใช้รูปแบบ TIMESTAMP.css นั้นดีกว่าโดยไม่ใช้สตริงข้อความค้นหาเนื่องจากมีความเป็นไปได้ที่ซอฟต์แวร์แคชพร็อกซีจะไม่แคชไฟล์
Tomas Andrle

34
น่าสังเกตไม่ว่าด้วยเหตุผลใดก็ตาม Stackoverflow เองใช้เมธอดสตริงการสืบค้น
jason

2
ตรวจสอบแล้วว่าการใช้? = พารามิเตอร์จะไม่ทำให้เบราว์เซอร์เรียกไฟล์แคชอีกครั้งเมื่อมีการเปลี่ยนแปลงพารามิเตอร์ วิธีเดียวคือการเปลี่ยนชื่อไฟล์ตัวเอง programatically ในตอนท้ายของเซิร์ฟเวอร์เป็นคำตอบโดยกีบ
arunskrish

41

ฉันได้ยินสิ่งนี้เรียกว่า "การกำหนดเวอร์ชันอัตโนมัติ" วิธีที่พบมากที่สุดคือการรวม mtime ของไฟล์คงที่ไว้ใน URL และตัดออกโดยใช้ตัวจัดการการเขียนซ้ำหรือ URL confs:

ดูสิ่งนี้ด้วย:


3
ขอบคุณฉันคิดว่านี่เป็นอีกกรณีหนึ่งที่ความคิดของฉันถูกกล่าวถึงฉันไม่ทราบว่ามันถูกเรียกดังนั้นฉันไม่เคยพบมันในการค้นหาของ Google
กีบ

27

คำตอบที่มีอยู่ 30 ข้อนั้นเป็นคำแนะนำที่ดีสำหรับเว็บไซต์ประมาณปี 2008 อย่างไรก็ตามเมื่อพูดถึงแอพพลิเคชั่นหน้าเดียวที่ทันสมัย(SPA) อาจถึงเวลาที่จะต้องทบทวนสมมติฐานขั้นพื้นฐานบางอย่าง…โดยเฉพาะความคิดที่เป็นที่ต้องการสำหรับเว็บเซิร์ฟเวอร์เพื่อให้บริการเฉพาะเวอร์ชันล่าสุด ไฟล์.

ลองนึกภาพคุณเป็นผู้ใช้ที่มีรุ่นMของ SPA โหลดลงในเบราว์เซอร์ของคุณ

  1. ท่อซีดีของคุณปรับรุ่นใหม่Nของแอพลิเคชันบนเซิร์ฟเวอร์
  2. คุณนำทางภายใน SPA ซึ่งส่ง XHR ไปยังเซิร์ฟเวอร์เพื่อรับ /some.template
    • (เบราว์เซอร์ของคุณไม่รีเฟรชหน้าดังนั้นคุณยังคงใช้งานเวอร์ชันM )
  3. เซิร์ฟเวอร์ตอบสนองด้วยเนื้อหาของ/some.template- คุณต้องการให้ส่งคืนเวอร์ชันMหรือNของเทมเพลตหรือไม่

หากรูปแบบการ/some.templateเปลี่ยนแปลงระหว่างรุ่นMและN (หรือไฟล์ถูกเปลี่ยนชื่อหรืออะไรก็ตาม) คุณอาจไม่ต้องการให้ส่งเทมเพลตรุ่นNไปยังเบราว์เซอร์ที่ใช้รุ่นMตัวแยกวิเคราะห์รุ่นเก่า

แอพของเว็บพบปัญหานี้เมื่อตรงตามเงื่อนไขสองข้อ:

  • ทรัพยากรถูกร้องขอแบบอะซิงโครนัสบางครั้งหลังจากโหลดหน้าเริ่มต้น
  • ตรรกะของแอปถือว่าสิ่งต่าง ๆ (ที่อาจเปลี่ยนแปลงในรุ่นอนาคต) เกี่ยวกับเนื้อหาของทรัพยากร

เมื่อแอปของคุณต้องการแสดงหลายเวอร์ชันพร้อมกันการแก้ปัญหาการแคชและ "โหลดซ้ำ" กลายเป็นเรื่องเล็กน้อย:

  1. ติดตั้งไฟล์ไซต์ทั้งหมดลงใน dirs เวอร์ชัน: /v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. ตั้งค่าส่วนหัว HTTP เพื่อให้ไฟล์แคชของเบราว์เซอร์คงอยู่ตลอดไป
    • (หรือดีกว่ายังใส่ทุกอย่างใน CDN)
  3. อัปเดตทั้งหมด<script>และ<link>แท็ก ฯลฯ เพื่อชี้ไปที่ไฟล์นั้นในหนึ่งใน dirs เวอร์ชัน

ขั้นตอนสุดท้ายนั้นฟังดูยุ่งยากเพราะอาจต้องมีการเรียกตัวสร้าง URL สำหรับทุก ๆ URL ในฝั่งเซิร์ฟเวอร์หรือรหัสฝั่งไคลเอ็นต์ของคุณ หรือคุณสามารถใช้<base>แท็กอย่างชาญฉลาดและเปลี่ยนเวอร์ชันปัจจุบันได้ในที่เดียว

†วิธีหนึ่งในการทำเช่นนี้คือการก้าวร้าวบังคับให้เบราว์เซอร์โหลดซ้ำทุกอย่างเมื่อมีการเปิดตัวเวอร์ชันใหม่ แต่เพื่อให้การดำเนินการใด ๆ ที่กำลังดำเนินการเสร็จสิ้นอาจยังคงง่ายที่สุดที่จะสนับสนุนอย่างน้อยสองรุ่นในแบบคู่ขนาน: v-current และ v-previous


ไมเคิล - ความคิดเห็นของคุณมีความเกี่ยวข้องมาก ฉันเข้ามาที่นี่พยายามค้นหาวิธีแก้ปัญหาสำหรับสปาของฉันอย่างแม่นยำ ฉันได้รับพอยน์เตอร์ แต่ก็ต้องหาวิธีแก้ปัญหาด้วยตัวเอง ในท้ายที่สุดฉันมีความสุขมากกับสิ่งที่เกิดขึ้นดังนั้นฉันจึงเขียนโพสต์บล็อกและตอบคำถามนี้ (รวมถึงรหัส) ขอบคุณสำหรับคำแนะนำ
statler

ความคิดเห็นที่ดี ฉันไม่สามารถเข้าใจได้ในขณะที่ผู้คนกำลังพูดถึงการใช้แคชและการแคช HTTP เป็นวิธีแก้ปัญหาการแคชเว็บไซต์อย่างแท้จริงโดยไม่ต้องพูดถึงปัญหาใหม่ของสปา
David Casillas

1
การตอบสนองที่ยอดเยี่ยมและกลยุทธ์ที่เหมาะอย่างยิ่ง! และคะแนนโบนัสสำหรับการกล่าวถึงbaseแท็ก! สำหรับการสนับสนุนโค้ดเก่า: มันไม่ได้เป็นไปได้เสมอไปและมันก็ไม่ใช่ความคิดที่ดีเสมอไป รหัสเวอร์ชันใหม่อาจรองรับการเปลี่ยนแปลงในส่วนอื่น ๆ ของแอพหรืออาจเกี่ยวข้องกับการแก้ไขฉุกเฉินช่องโหว่แพทช์และอื่น ๆ ฉันยังไม่ได้ใช้กลยุทธ์นี้ด้วยตนเอง แต่ฉันรู้สึกเสมอว่าสถาปัตยกรรมโดยรวมควรอนุญาตให้ deploys ติดแท็กเวอร์ชันเก่าเป็นobsoleteและบังคับให้โหลดซ้ำในครั้งถัดไปที่มีการโทรแบบอะซิงโครนัส (หรือเพียงแค่บังคับให้ทุกเซสชันผ่าน WebSockets) )
Jonny Asmar

ยินดีที่ได้เห็นคำตอบที่คิดอย่างดีเกี่ยวกับแอปพลิเคชันหน้าเดียว
เนทฉัน

นั่นคือ "การปรับใช้สีน้ำเงิน - เขียว" ถ้าคุณต้องการค้นหาข้อมูลเพิ่มเติม
Fil

15

อย่าใช้ foo.css? version = 1! เบราว์เซอร์ไม่ควรแคช URL ด้วยตัวแปร GET อ้างอิงจากhttp://www.thinkvitamin.com/features/webapps/serving-javascript-fastแม้ว่า IE และ Firefox จะเพิกเฉยต่อสิ่งนี้ Opera และ Safari ก็ไม่ทำเช่นนั้น! ใช้ foo.v1234.css แทนและใช้กฎการเขียนซ้ำเพื่อดึงหมายเลขเวอร์ชันออก


1
ก่อนอื่นเบราว์เซอร์ทั้งหมดจะไม่แคชนั่นคือหน้าที่ของ HTTP เหตุใด http จึงสนใจเกี่ยวกับโครงสร้างของ URI มีเจ้าหน้าที่อ้างอิงถึงข้อมูลจำเพาะที่ระบุว่าการแคช HTTP ควรเข้าใจความหมายของ URI เพื่อที่จะไม่แคชรายการด้วยสตริงข้อความค้นหาหรือไม่
AnthonyWJones

13
เว็บเบราว์เซอร์ที่มีฟังก์ชั่นการแคชวัตถุ (ตรวจสอบไดเรกทอรีแคชของเบราว์เซอร์ของคุณ) HTTP เป็นโปรโตคอลที่รวมถึงคำสั่งจากเซิร์ฟเวอร์ไปยังไคลเอนต์ (พร็อกซีเบราว์เซอร์สไปเดอร์ ฯลฯ ) แนะนำการควบคุมแคช
tzot

13

ใน Laravel (PHP) เราสามารถทำได้ตามวิธีที่ชัดเจนและสง่างาม (โดยใช้การประทับเวลาการแก้ไขไฟล์):

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>

และคล้ายกับ CSS

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">

ตัวอย่างเอาท์พุท html ( filemtimeเวลากลับมาเป็นUnix timestamp )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">

ผลลัพธ์ของคำสั่งนี้ใน html คืออะไร? และถ้าฉันจำเป็นต้องต่ออายุเฉพาะเวอร์ชันเช่น? v = 3,? v = 4 และอื่น ๆ - ไม่บังคับให้เบราว์เซอร์โหลด css ทุกครั้งที่ผู้ใช้เข้าสู่เว็บไซต์
Gediminas

filemtime : "ฟังก์ชั่นนี้จะคืนค่าเวลาเมื่อบล็อกข้อมูลของไฟล์ถูกเขียนไปนั่นคือเวลาที่เนื้อหาของไฟล์ถูกเปลี่ยน" src: php.net/manual/en/function.filemtime.php
Kamil Kiełczewski

11

RewriteRule ต้องการการอัพเดตเล็กน้อยสำหรับไฟล์ js หรือ css ที่มีการจัดทำเครื่องหมายจุดในตอนท้าย เช่น json-1.3.js

ฉันเพิ่มระดับจุดลบ [^.] ใน regex ดังนั้น. จะถูกละเว้น

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]

2
ขอบคุณสำหรับการเข้า! ตั้งแต่ฉันเขียนโพสต์นี้ฉันก็ถูกเผาด้วยเช่นกัน ทางออกของฉันคือการเขียนใหม่เฉพาะในส่วนสุดท้ายของชื่อไฟล์ที่มีสิบหลัก (10 หลักครอบคลุมการประทับเวลาทั้งหมดตั้งแต่ 9/9/2001 ถึง 11/20/2286) ฉันได้อัปเดตคำตอบของฉันเพื่อรวม regex นี้:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip

ฉันเข้าใจ regex แต่ฉันไม่เข้าใจว่าคุณกำลังแก้ไขปัญหา[^.]อะไร นอกจากนี้ยังไม่มีประโยชน์ที่จะเขียน\dในชั้นเรียนของตัวละคร - \d+จะทำสิ่งเดียวกัน ในขณะที่โพสต์รูปแบบของคุณจะจับคู่จำนวนอักขระใด ๆ (อย่างตะกละตะกลาม) ตามด้วยตัวอักษรตามด้วยจุดที่ไม่ใช่จุดแล้วหนึ่งหรือมากกว่าหนึ่งหลักแล้วจุดแล้วcssหรือjsจากนั้นท้ายชื่อไฟล์ ไม่มีการจับคู่สำหรับอินพุตตัวอย่างของคุณ: regex101.com/r/RPGC62/1
mickmackusa

10

สำหรับ ASP.NET 4.5 และสูงกว่าคุณสามารถใช้สคริปต์บันเดิลได้

คำร้องขอhttp://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81สำหรับบันเดิล AllMyScripts และมีคู่สตริงเคียวรี v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 เคียวรีสตริง v มีโทเค็นค่าที่เป็นตัวระบุเฉพาะที่ใช้สำหรับการแคช ตราบใดที่ชุดข้อมูลไม่เปลี่ยนแปลงแอปพลิเคชัน ASP.NET จะขอชุดข้อมูล AllMyScripts ที่ใช้โทเค็นนี้ หากไฟล์ใด ๆ ในการเปลี่ยนแปลงบันเดิลเฟรมเวิร์กการออปติไมซ์ ASP.NET จะสร้างโทเค็นใหม่โดยรับประกันว่าเบราว์เซอร์ที่ร้องขอสำหรับบันเดิลจะได้รับบันเดิลล่าสุด

มีประโยชน์อื่น ๆ สำหรับการรวมรวมถึงประสิทธิภาพที่เพิ่มขึ้นในการโหลดหน้าเว็บครั้งแรกด้วยการลดขนาด


โปรดช่วยฉันฉันไม่ได้ทำการเปลี่ยนแปลงใด ๆ ใน bundle.config เพียงแค่เปลี่ยนไฟล์ css หรือ js แล้วฉันจะแก้ไขปัญหาการแคชได้อย่างไร
vedankita kumbhar

10

นี่คือโซลูชัน JavaScript ที่แท้จริง

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

ด้านบนจะค้นหาครั้งสุดท้ายที่ผู้ใช้เยี่ยมชมไซต์ของคุณ หากการเข้าชมครั้งสุดท้ายก่อนที่คุณจะเปิดตัวรหัสใหม่มันจะใช้location.reload(true)เพื่อบังคับให้รีเฟรชหน้าเว็บจากเซิร์ฟเวอร์

ฉันมักจะมีสิ่งนี้เป็นสคริปต์แรกใน <head>ดังนั้นจึงมีการประเมินก่อนโหลดเนื้อหาอื่น ๆ หากจำเป็นต้องโหลดซ้ำจะเห็นได้ชัดว่าผู้ใช้แทบจะไม่เห็น

ฉันใช้พื้นที่เก็บข้อมูลในเครื่องเพื่อจัดเก็บเวลาบันทึกการเยี่ยมชมครั้งล่าสุดบนเบราว์เซอร์ แต่คุณสามารถเพิ่มคุกกี้ลงในมิกซ์ได้หากคุณต้องการสนับสนุน IE เวอร์ชันเก่ากว่า


ฉันลองอะไรเช่นนี้มันจะใช้ได้เฉพาะกับเพจที่โหลดซ้ำ แต่ถ้าไซต์มีหลายเพจที่ใช้ css / ภาพเหมือนกันหน้าอื่น ๆ ก็จะยังคงใช้ทรัพยากรเก่าอยู่
DeepBlue

9

โพสต์ที่น่าสนใจ ต้องอ่านคำตอบทั้งหมดที่นี่รวมกับความจริงที่ว่าฉันไม่เคยมีปัญหาใด ๆ กับสตริงข้อความค้นหา "ปลอม" (ซึ่งฉันไม่แน่ใจว่าทำไมทุกคนไม่เต็มใจที่จะใช้สิ่งนี้) ฉันเดาวิธีแก้ปัญหา เช่นเดียวกับคำตอบที่ยอมรับ) คือการคำนวณแฮชสั้น ๆ ของเนื้อหาไฟล์ CSS (แทนที่จะเป็นไฟล์วันที่และเวลา) เพื่อเป็นการสอบถามแบบปลอม

สิ่งนี้จะส่งผลต่อไปนี้:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

แน่นอนว่าโซลูชัน datetime ยังทำงานในกรณีของการแก้ไขไฟล์ CSS แต่ฉันคิดว่ามันเป็นเรื่องเกี่ยวกับเนื้อหาไฟล์ css และไม่เกี่ยวกับวันที่ของไฟล์ดังนั้นทำไมพวกนี้ถึงสับสน?


8

สำหรับการพัฒนาของฉันฉันพบว่าโครเมี่ยมมีทางออกที่ดี

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

ด้วยเครื่องมือของนักพัฒนาซอฟต์แวร์ที่เปิดอยู่เพียงแค่คลิกปุ่มรีเฟรชและปล่อยให้ไปเมื่อคุณวางเมาส์เหนือ "Empty Cache และ Hard Reload"

นี่คือเพื่อนที่ดีที่สุดของฉันและเป็นวิธีที่เบามากในการได้รับสิ่งที่คุณต้องการ!


และหากคุณใช้ Chrome เป็นสภาพแวดล้อมในการพัฒนาโซลูชันที่ไม่ต้องบุกรุกอีกวิธีหนึ่งคือการปิดการใช้งานแคช: ภายใต้การตั้งค่าฟันเฟืองคุณสามารถทำให้ดิสก์แคชใช้ไม่ได้โดยการเลือก 'ปิดใช้งานแคช' (หมายเหตุ: DevTools เพื่อให้ทำงานได้)
Velojet

7

ขอบคุณที่ Kip สำหรับโซลูชั่นที่สมบูรณ์แบบของเขา!

ฉันขยายเพื่อใช้เป็น Zend_view_Helper เนื่องจากไคลเอนต์ของฉันเรียกใช้เพจของเขาบนโฮสต์เสมือนฉันยังขยายมันสำหรับที่

หวังว่ามันจะช่วยคนอื่นด้วย

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

ไชโยและขอบคุณ


7

ไม่พบวิธีการใช้งาน DOM ฝั่งไคลเอ็นต์ในการสร้างองค์ประกอบสคริปต์โหนด (หรือ css) แบบไดนามิก:

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>

6

google chrome มีฮาร์ดโหลดเช่นเดียวกับแคชเปล่าและตัวเลือกฮาร์ดโหลดคุณสามารถคลิกปุ่มโหลดซ้ำ (ในโหมดตรวจสอบ) เพื่อเลือก


เพื่อชี้แจงโดย "โหมดตรวจสอบ" พวกเขาอ้างถึง "เครื่องมือ Dev" หรือที่รู้จักว่า F12, aka ctrl + shift + i, aka ant menu > More Tools> Developer Toolsอาคา>right click Inspect Elementนอกจากนี้ยังมีการตั้งค่าฝังอยู่ที่ไหนสักแห่งในเครื่องมือ dev (ฉันลืมตำแหน่ง) เพื่อโหลดซ้ำอย่างหนักในทุก ๆ การโหลด
Jonny Asmar

5

คุณสามารถบังคับให้ "แคชทั้งเซสชัน" หากคุณเพิ่ม session-id เป็นพารามิเตอร์ spureous ของไฟล์ js / css:

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

หากคุณต้องการแคชทั้งรุ่นคุณสามารถเพิ่มรหัสบางอย่างเพื่อพิมพ์วันที่ของไฟล์หรือที่คล้ายกัน หากคุณกำลังใช้ Java คุณสามารถใช้แท็กที่กำหนดเองเพื่อสร้างลิงก์อย่างหรูหรา

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>

5

สมมติว่าคุณมีไฟล์อยู่ที่:

/styles/screen.css

คุณสามารถเพิ่มพารามิเตอร์การสืบค้นด้วยข้อมูลรุ่นลงใน URI เช่น:

/styles/screen.css?v=1234

หรือคุณสามารถเพิ่มข้อมูลรุ่นเช่น:

/v/1234/styles/screen.css

IMHO วิธีที่สองจะดีกว่าสำหรับไฟล์ CSS เพราะพวกเขาสามารถอ้างถึงรูปภาพโดยใช้ URL สัมพัทธ์ซึ่งหมายความว่าถ้าคุณระบุสิ่งที่background-imageชอบ:

body {
    background-image: url('images/happy.gif');
}

URL จะมีประสิทธิภาพ:

/v/1234/styles/images/happy.gif

ซึ่งหมายความว่าหากคุณอัปเดตหมายเลขเวอร์ชันที่ใช้เซิร์ฟเวอร์จะถือว่านี่เป็นทรัพยากรใหม่และไม่ใช้เวอร์ชันแคช หากคุณอ้างอิงหมายเลขเวอร์ชันของคุณใน Subversion / CVS / etc การแก้ไขนี้หมายความว่าการเปลี่ยนแปลงภาพที่อ้างอิงในไฟล์ CSS จะถูกสังเกตเห็น ไม่รับประกันกับแบบแผนแรกเช่น URL ที่images/happy.gifเกี่ยวข้อง/styles/screen.css?v=1235มี/styles/images/happy.gifซึ่งไม่ได้มีข้อมูลเกี่ยวกับรุ่นใด ๆ

ฉันใช้โซลูชันแคชโดยใช้เทคนิคนี้กับ Java servlets และจัดการกับคำร้องขอ/v/*กับเซิร์ฟเล็ตที่มอบหมายให้กับทรัพยากรพื้นฐาน (เช่น/styles/screen.css) ในโหมดการพัฒนาผมตั้งแคชส่วนหัวที่บอกลูกค้าที่จะมักจะตรวจสอบความสดของทรัพยากรที่มีเซิร์ฟเวอร์ (นี้มักจะส่งผลใน 304 ถ้าคุณมอบหมายให้ของ Tomcat DefaultServletและ.css, .jsฯลฯ ไฟล์ไม่ได้เปลี่ยน) ในขณะที่อยู่ในโหมดการใช้งาน ฉันตั้งค่าส่วนหัวที่พูดว่า "แคชถาวร"


เพียงแค่เพิ่มโฟลเดอร์ที่คุณสามารถเปลี่ยนชื่อเมื่อจำเป็นจะใช้งานได้ถ้าคุณใช้เฉพาะ URL ที่เกี่ยวข้อง และจากนั้นคุณให้แน่ใจว่าจะเปลี่ยนเส้นทางไปยังโฟลเดอร์ที่เหมาะสมจากโฟลเดอร์ฐานเช่นใน <?php header( 'Location: folder1/login.phtml' ); ?>PHP:
Gruber

1
การใช้วิธีที่สองการเปลี่ยน CSS จะทำให้สำเนาที่เก็บไว้ในแคชของรูปภาพทั้งหมดที่อ้างอิงกับ URL สัมพัทธ์ซึ่งอาจเป็นหรือไม่ต้องการก็ได้
TomG


5

สำหรับ ASP.NET ฉันคิดว่าการแก้ปัญหาต่อไปด้วยตัวเลือกขั้นสูง (โหมดการดีบัก / ปล่อยรุ่น):

ไฟล์ Js หรือ Css รวมอยู่ด้วยวิธีดังกล่าว:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix และ Global.CssPostfix คำนวณโดยวิธีต่อไปนี้ใน Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}

4

ฉันเพิ่งแก้ปัญหานี้โดยใช้ Python ที่นี่รหัส (ควรจะง่ายต่อการใช้กับภาษาอื่น ๆ ):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

รหัสนี้โดยทั่วไปจะเพิ่มการประทับเวลาของไฟล์เป็นพารามิเตอร์แบบสอบถามต่อท้าย URL การเรียกใช้ฟังก์ชันต่อไปนี้

script("/main.css")

จะส่งผลให้

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

ข้อได้เปรียบของหลักสูตรคือคุณไม่จำเป็นต้องเปลี่ยน html ของคุณอีกครั้งการสัมผัสไฟล์ CSS จะทำให้การทำให้แคชใช้ไม่ได้โดยอัตโนมัติ ใช้งานได้ดีมากและค่าใช้จ่ายไม่ชัดเจน


os.stat () สามารถสร้างคอขวดได้หรือไม่?
hoju

@Richard stat อาจเป็นคอขวดหากดิสก์ช้ามากและคำขอมีจำนวนมาก ในกรณีนี้คุณสามารถแคชการประทับเวลาที่ไหนสักแห่งในหน่วยความจำและล้างแคชนี้เมื่อมีการปรับใช้ใหม่ทุกครั้ง แต่ความซับซ้อนนี้จะไม่จำเป็นในกรณีส่วนใหญ่ใช้
ปี่

4

หากคุณใช้ git + PHP คุณสามารถโหลดสคริปต์ใหม่จากแคชในแต่ละครั้งที่มีการเปลี่ยนแปลงใน git repo โดยใช้รหัสต่อไปนี้:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;

4

หากคุณเป็นนักพัฒนาที่ต้องการหลีกเลี่ยงการแคชแท็บเครือข่ายของ Chrome จะปิดใช้งานตัวเลือกแคช มิฉะนั้นคุณสามารถทำได้โดยไม่มีเฟรมเวิร์กการเรนเดอร์เซิร์ฟเวอร์โดยใช้สองแท็กสคริปต์

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>

4

คำถามนี้เก่ามากและปรากฏเป็นสิ่งแรกเมื่อใครก็ตามที่จัดการกับปัญหานี้ นี่ไม่ใช่คำตอบสำหรับคำถามที่ทาง op ต้องการ แต่แทนที่จะเป็นคำตอบของ devs กับปัญหานี้ในขณะที่กำลังพัฒนาและทดสอบ และฉันไม่สามารถโพสต์คำถามใหม่ในหัวข้อนี้เนื่องจากจะถูกทำเครื่องหมายว่าซ้ำกัน

เช่นเดียวกับคนอื่น ๆ ฉันต้องการลบแคชสั้น ๆ

"keep caching consistent with the file" .. ทางมันยุ่งยากเกินไป ..

โดยทั่วไปแล้วฉันไม่รังเกียจที่จะโหลดมากขึ้น - แม้แต่การโหลดไฟล์อีกครั้งซึ่งไม่เปลี่ยนแปลง - ในโครงการส่วนใหญ่ - ไม่เกี่ยวข้องกับการใช้งานจริง ในขณะที่กำลังพัฒนาแอป - เราส่วนใหญ่โหลดจากดิสก์อยู่localhost:port - ดังนั้นincrease in network trafficปัญหานี้จึงไม่ใช่ปัญหาที่เกิดขึ้นขึ้น

โครงการขนาดเล็กส่วนใหญ่เป็นเพียงการเล่นรอบ - พวกเขาไม่เคยจบลงในการผลิต ดังนั้นสำหรับพวกเขาคุณไม่ต้องการอะไรอีกแล้ว ..

เช่นถ้าคุณใช้Chrome Dev Toolsคุณสามารถทำตามวิธีการปิดใช้งานการแคชเช่นในภาพด้านล่าง: วิธีบังคับให้ Chrome โหลดไฟล์แคชซ้ำ

และหากคุณมีปัญหาการแคชของFirefox : วิธีบังคับให้โหลดเนื้อหาซ้ำบน firefox

วิธีปิดการใช้งานแคชใน Firefox ขณะที่กำลังพัฒนา ทำสิ่งนี้เท่านั้นในการพัฒนาคุณต้องมีกลไกในการบังคับให้โหลดซ้ำสำหรับการผลิตเนื่องจากผู้ใช้ของคุณจะใช้โมดูลที่ไม่ถูกต้องของแคชเก่าหากคุณอัปเดตแอปของคุณบ่อยครั้งและคุณไม่ได้เตรียมกลไกการซิงค์แคชโดยเฉพาะ ข้างบน.

ใช่ข้อมูลนี้มีอยู่แล้วในคำตอบก่อนหน้านี้ แต่ฉันยังต้องทำการค้นหา google เพื่อค้นหามัน

หวังว่าคำตอบนี้ชัดเจนมากและตอนนี้คุณไม่จำเป็นต้อง


OP ถามอะไรซักอย่างแล้วตอบอย่างอื่น มันไม่เกี่ยวกับการโหลดแรงในท้องถิ่น แต่ในการผลิตและคุณไม่สามารถขอให้ผู้ใช้ปลายทางตามข้างบนเพื่อปิดการใช้งานแคช ฯลฯ
Jitendra Pancholi

3

ดูเหมือนว่าคำตอบทั้งหมดที่นี่แนะนำการกำหนดเวอร์ชันในรูปแบบการตั้งชื่อซึ่งมีข้อเสีย

เบราว์เซอร์ควรตระหนักถึงสิ่งที่จะแคชและสิ่งที่ไม่แคชโดยการอ่านการตอบสนองของเว็บเซิร์ฟเวอร์โดยเฉพาะอย่างยิ่งส่วนหัว http - ทรัพยากรนี้ใช้งานได้นานเท่าไหร่? ทรัพยากรนี้อัปเดตตั้งแต่ฉันดึงข้อมูลครั้งล่าสุดหรือไม่ เป็นต้น

หากสิ่งต่าง ๆ ได้รับการกำหนดค่าอย่างถูกต้องเพียงอัปเดตไฟล์แอปพลิเคชันของคุณควรรีเฟรชแคชเบราว์เซอร์ของคุณ ตัวอย่างเช่นคุณสามารถกำหนดค่าเว็บเซิร์ฟเวอร์ของคุณเพื่อบอกให้เบราว์เซอร์ไม่เคยแคชไฟล์ (ซึ่งเป็นแนวคิดที่ไม่ดี)

คำอธิบายเชิงลึกเพิ่มเติมเกี่ยวกับการทำงานของมันอยู่ที่นี่ https://www.mnot.net/cache_docs/#WORK


3

เพียงเพิ่มรหัสนี้ซึ่งคุณต้องการโหลดซ้ำอย่างหนัก (บังคับให้เบราว์เซอร์โหลดไฟล์ CSS / JS ที่แคชไว้) ทำสิ่งนี้ในไฟล์. load เพื่อไม่ให้รีเฟรชเหมือนวง

 $( window ).load(function() {
   location.reload(true);
});

ไม่ทำงานบน Chrome ยังคงโหลดเนื้อหาจากดิสก์แคช
Jason Kim

3

เพียงใช้รหัสฝั่งเซิร์ฟเวอร์เพื่อเพิ่มวันที่ของไฟล์ ... วิธีนี้จะถูกแคชและโหลดใหม่เมื่อไฟล์มีการเปลี่ยนแปลง

ใน ASP.NET

<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    

สิ่งนี้สามารถทำให้ง่ายขึ้นเพื่อ:

<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>

โดยการเพิ่มวิธีการขยายในโครงการของคุณเพื่อขยายหน้า:

public static class Extension_Methods
{
    public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
    {
        string sFilePath = oPg.Server.MapPath(sRelPath);
        string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
        string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");

        return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
    }
}

2

ฉันขอแนะนำให้ใช้กระบวนการต่อไปนี้:

  • รุ่นไฟล์ css / js ของคุณทุกครั้งที่คุณปรับใช้บางอย่างเช่น: screen.1233.css (หมายเลขสามารถแก้ไข SVN ของคุณหากคุณใช้ระบบการกำหนดรุ่น)

  • ย่อให้เล็กสุดเพื่อเพิ่มประสิทธิภาพเวลาในการโหลด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.